mirror of
https://github.com/DragonOS-Community/DragonOS.git
synced 2025-06-08 14:16:47 +00:00
feat(user): user management tool (#825)
* 用户管理工具 * 重构 * 改为多个bin文件入口 * bin文件的usage显示自身程序名而非固定程序名
This commit is contained in:
parent
415e14e9c3
commit
03746da3d9
3
user/apps/user-manage/.gitignore
vendored
Normal file
3
user/apps/user-manage/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
/target
|
||||
/Cargo.lock
|
||||
/install
|
36
user/apps/user-manage/Cargo.toml
Normal file
36
user/apps/user-manage/Cargo.toml
Normal file
@ -0,0 +1,36 @@
|
||||
[package]
|
||||
name = "user_manage_tool"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[[bin]]
|
||||
name = "useradd"
|
||||
path = "src/cmd/useradd.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "userdel"
|
||||
path = "src/cmd/userdel.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "usermod"
|
||||
path = "src/cmd/usermod.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "passwd"
|
||||
path = "src/cmd/passwd.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "groupadd"
|
||||
path = "src/cmd/groupadd.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "groupdel"
|
||||
path = "src/cmd/groupdel.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "groupmod"
|
||||
path = "src/cmd/groupmod.rs"
|
||||
|
||||
[dependencies]
|
||||
libc = "0.2.153"
|
||||
lazy_static = "1.4.0"
|
47
user/apps/user-manage/Makefile
Normal file
47
user/apps/user-manage/Makefile
Normal file
@ -0,0 +1,47 @@
|
||||
# The toolchain we use.
|
||||
# You can get it by running DragonOS' `tools/bootstrap.sh`
|
||||
TOOLCHAIN="+nightly-2023-08-15-x86_64-unknown-linux-gnu"
|
||||
RUSTFLAGS+=""
|
||||
|
||||
ifdef DADK_CURRENT_BUILD_DIR
|
||||
# 如果是在dadk中编译,那么安装到dadk的安装目录中
|
||||
INSTALL_DIR = $(DADK_CURRENT_BUILD_DIR)
|
||||
else
|
||||
# 如果是在本地编译,那么安装到当前目录下的install目录中
|
||||
INSTALL_DIR = ./install
|
||||
endif
|
||||
|
||||
|
||||
ifeq ($(ARCH), x86_64)
|
||||
export RUST_TARGET=x86_64-unknown-linux-musl
|
||||
else ifeq ($(ARCH), riscv64)
|
||||
export RUST_TARGET=riscv64gc-unknown-linux-gnu
|
||||
else
|
||||
# 默认为x86_86,用于本地编译
|
||||
export RUST_TARGET=x86_64-unknown-linux-musl
|
||||
endif
|
||||
|
||||
build:
|
||||
RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) build --target $(RUST_TARGET)
|
||||
|
||||
run-dragonreach:
|
||||
RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) run --target $(RUST_TARGET) --bin DragonReach
|
||||
|
||||
clean:
|
||||
RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) clean
|
||||
|
||||
build-release:
|
||||
RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) build --target $(RUST_TARGET) --release
|
||||
|
||||
clean-release:
|
||||
RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) clean --target $(RUST_TARGET) --release
|
||||
|
||||
fmt:
|
||||
RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) fmt
|
||||
|
||||
fmt-check:
|
||||
RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) fmt --check
|
||||
|
||||
.PHONY: install
|
||||
install:
|
||||
RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) install --target $(RUST_TARGET) --path . --no-track --root $(INSTALL_DIR) --force
|
142
user/apps/user-manage/README.md
Normal file
142
user/apps/user-manage/README.md
Normal file
@ -0,0 +1,142 @@
|
||||
## useradd
|
||||
|
||||
- usage:添加用户
|
||||
|
||||
> useradd [options] username
|
||||
|
||||
useradd -c \<comment\> -d \<home\> -G \<group\> -g \<gid\> -s \<shell\> -u \<uid\> username
|
||||
|
||||
- 参数说明:
|
||||
|
||||
- 选项:
|
||||
-c comment 指定一段注释性描述
|
||||
-d 目录 指定用户主目录,如果不存在,则创建该目录
|
||||
-G 用户组 指定用户所属的用户组
|
||||
-g 组id
|
||||
-s Shell 文件 指定用户的登录 Shell
|
||||
-u 用户号 指定用户的用户号
|
||||
|
||||
- 用户名:
|
||||
指定新账号的登录名。
|
||||
|
||||
- 更新文件:
|
||||
> /etc/passwd
|
||||
> /etc/shadow
|
||||
> /etc/group
|
||||
> /etc/gshadow
|
||||
|
||||
## userdel
|
||||
|
||||
- usage:删除用户
|
||||
|
||||
> userdel [options] username
|
||||
|
||||
userdel -r username
|
||||
|
||||
- 选项:
|
||||
-r 连同用户主目录一起删除。
|
||||
|
||||
- 更新文件:
|
||||
> /etc/passwd
|
||||
> /etc/shadow
|
||||
> /etc/group
|
||||
|
||||
## usermod
|
||||
|
||||
- usage:修改用户
|
||||
|
||||
> usermod [options] username
|
||||
|
||||
usermod -a -G<组 1,组 2,...> -c<备注> -d<登入目录> -G<组名> -l<名称> -s<登入终端> -u<用户 id> username
|
||||
|
||||
- 选项:
|
||||
-a -G<组 1,组 2,...> 将用户添加到其它组中
|
||||
-c<备注> 修改用户帐号的备注文字。
|
||||
-d 登入目录> 修改用户登入时的目录。
|
||||
-G<组名> 修改用户所属的群组。
|
||||
-l<名称> 修改用户名称。
|
||||
-s\<shell\> 修改用户登入后所使用的 shell。
|
||||
-u\<uid\> 修改用户 ID。
|
||||
|
||||
- 更新文件:
|
||||
> /etc/passwd
|
||||
> /etc/shadow
|
||||
> /etc/group
|
||||
> /etc/gshadow
|
||||
|
||||
## passwd
|
||||
|
||||
- usage:设置密码
|
||||
|
||||
> 普通用户: passwd
|
||||
> root 用户: passwd username
|
||||
|
||||
普通用户只能修改自己的密码,因此不需要指定用户名。
|
||||
|
||||
- 更新文件
|
||||
> /etc/shadow
|
||||
> /etc/passwd
|
||||
|
||||
## groupadd
|
||||
|
||||
- usage:添加用户组
|
||||
|
||||
> groupadd [options] groupname
|
||||
|
||||
groupadd -g\<gid\> -p\<passwd\> groupname
|
||||
|
||||
- 选项:
|
||||
-g\<gid\> 指定组 id
|
||||
-p 设置密码
|
||||
|
||||
- 更新文件
|
||||
> /etc/group
|
||||
> /etc/gshadow
|
||||
|
||||
## groupdel
|
||||
|
||||
- usage:删除用户组
|
||||
|
||||
> groupdel groupname
|
||||
|
||||
groupdel \<groupname\>
|
||||
|
||||
- 注意事项:
|
||||
只有当用户组的组成员为空时才可以删除该组
|
||||
|
||||
- 更新文件
|
||||
> /etc/group
|
||||
> /etc/gshadow
|
||||
|
||||
## groupmod
|
||||
|
||||
- usage:修改用户组信息
|
||||
|
||||
> groupmod [options] groupname
|
||||
|
||||
groupadd -g\<new gid\> -n\<new groupname\> groupname
|
||||
|
||||
- 选项:
|
||||
-g 设置新 gid
|
||||
-n 设置新组名
|
||||
|
||||
- 更新文件
|
||||
> /etc/group
|
||||
> /etc/gshadow
|
||||
> /etc/passwd
|
||||
|
||||
_/etc/passwd 文件格式:_
|
||||
|
||||
> 用户名:口令:用户标识号:组标识号:注释性描述:主目录:登录 Shell
|
||||
|
||||
_/etc/shadow 文件格式:_
|
||||
|
||||
> 登录名:加密口令:最后一次修改时间:最小时间间隔:最大时间间隔:警告时间:不活动时间:失效时间:标志
|
||||
|
||||
_/etc/group 文件格式:_
|
||||
|
||||
> 组名:口令:组标识号:组内用户列表
|
||||
|
||||
_/etc/gshadow 文件格式:_
|
||||
|
||||
> 组名:组密码:组管理员名称:组成员
|
908
user/apps/user-manage/src/check/check.rs
Normal file
908
user/apps/user-manage/src/check/check.rs
Normal file
@ -0,0 +1,908 @@
|
||||
use super::info::{GAddInfo, GDelInfo, GModInfo, PasswdInfo, UAddInfo, UDelInfo, UModInfo};
|
||||
use crate::{
|
||||
error::error::{ErrorHandler, ExitStatus},
|
||||
parser::cmd::{CmdOption, GroupCommand, PasswdCommand, UserCommand},
|
||||
};
|
||||
use std::{
|
||||
collections::{HashMap, HashSet},
|
||||
fs,
|
||||
io::Write,
|
||||
};
|
||||
|
||||
/// useradd命令检查器
|
||||
#[derive(Debug)]
|
||||
pub struct UAddCheck;
|
||||
|
||||
impl UAddCheck {
|
||||
/// **校验解析后的useradd命令**
|
||||
///
|
||||
/// ## 参数
|
||||
/// - `cmd`: 解析后的useradd命令
|
||||
///
|
||||
/// ## 返回
|
||||
/// - `UAddInfo`: 校验后的信息
|
||||
pub fn check(cmd: UserCommand) -> UAddInfo {
|
||||
let mut info = UAddInfo::default();
|
||||
info.username = cmd.username;
|
||||
|
||||
// 填充信息
|
||||
for (option, arg) in cmd.options.iter() {
|
||||
match option {
|
||||
CmdOption::Shell => {
|
||||
info.shell = arg.clone();
|
||||
}
|
||||
CmdOption::Comment => {
|
||||
info.comment = arg.clone();
|
||||
}
|
||||
CmdOption::Uid => {
|
||||
info.uid = arg.clone();
|
||||
}
|
||||
CmdOption::Group => {
|
||||
info.group = arg.clone();
|
||||
}
|
||||
CmdOption::Gid => {
|
||||
info.gid = arg.clone();
|
||||
}
|
||||
CmdOption::Dir => {
|
||||
info.home_dir = arg.clone();
|
||||
}
|
||||
_ => {
|
||||
let op: &str = option.clone().into();
|
||||
ErrorHandler::error_handle(
|
||||
format!("Unimplemented option: {}", op),
|
||||
ExitStatus::InvalidCmdSyntax,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 完善用户信息
|
||||
if info.username.is_empty() {
|
||||
ErrorHandler::error_handle("Invalid username".to_string(), ExitStatus::InvalidArg);
|
||||
}
|
||||
|
||||
if info.uid.is_empty() {
|
||||
ErrorHandler::error_handle("Uid is required".to_string(), ExitStatus::InvalidCmdSyntax);
|
||||
}
|
||||
|
||||
if info.comment.is_empty() {
|
||||
info.comment = info.username.clone() + ",,,";
|
||||
}
|
||||
if info.home_dir.is_empty() {
|
||||
let home_dir = format!("/home/{}", info.username.clone());
|
||||
info.home_dir = home_dir;
|
||||
}
|
||||
if info.shell.is_empty() {
|
||||
info.shell = "/bin/NovaShell".to_string();
|
||||
}
|
||||
|
||||
// 校验终端是否有效
|
||||
check_shell(&info.shell);
|
||||
|
||||
// 校验是否有重复用户名和用户id
|
||||
scan_passwd(
|
||||
PasswdField {
|
||||
username: Some(info.username.clone()),
|
||||
uid: Some(info.uid.clone()),
|
||||
},
|
||||
false,
|
||||
);
|
||||
|
||||
// 判断group和gid是否有效
|
||||
Self::check_group_gid(&mut info);
|
||||
|
||||
info
|
||||
}
|
||||
|
||||
/// 检查组名、组id是否有效,如果组名不存在,则创建新的用户组
|
||||
fn check_group_gid(info: &mut UAddInfo) {
|
||||
if info.group.is_empty() && info.gid.is_empty() {
|
||||
ErrorHandler::error_handle(
|
||||
"user must belong to a group".to_string(),
|
||||
ExitStatus::InvalidCmdSyntax,
|
||||
);
|
||||
}
|
||||
|
||||
let r = fs::read_to_string("/etc/group");
|
||||
let mut max_gid: u32 = 0;
|
||||
match r {
|
||||
Ok(content) => {
|
||||
for line in content.lines() {
|
||||
let data: Vec<&str> = line.split(":").collect();
|
||||
let (groupname, gid) = (data[0].to_string(), data[2].to_string());
|
||||
if !info.group.is_empty() && info.group == groupname {
|
||||
if !info.gid.is_empty() && info.gid != gid {
|
||||
ErrorHandler::error_handle(
|
||||
format!("The gid of the group [{}] isn't {}", info.group, info.gid),
|
||||
ExitStatus::InvalidArg,
|
||||
)
|
||||
} else if info.gid.is_empty() || info.gid == gid {
|
||||
info.gid = gid;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if !info.gid.is_empty() && info.gid == gid {
|
||||
if !info.group.is_empty() && info.group != groupname {
|
||||
ErrorHandler::error_handle(
|
||||
format!("The gid of the group [{}] isn't {}", info.group, info.gid),
|
||||
ExitStatus::InvalidArg,
|
||||
)
|
||||
} else if info.group.is_empty() || info.group == groupname {
|
||||
info.group = groupname;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
max_gid = max_gid.max(u32::from_str_radix(data[2], 10).unwrap());
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
ErrorHandler::error_handle(
|
||||
"Can't read file: /etc/group".to_string(),
|
||||
ExitStatus::GroupFile,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 没有对应的用户组,默认创建新的用户组
|
||||
let mut groupname = info.username.clone();
|
||||
let mut gid = (max_gid + 1).to_string();
|
||||
if !info.group.is_empty() {
|
||||
groupname = info.group.clone();
|
||||
} else {
|
||||
info.group = groupname.clone();
|
||||
}
|
||||
|
||||
if !info.gid.is_empty() {
|
||||
gid = info.gid.clone();
|
||||
} else {
|
||||
info.gid = gid.clone();
|
||||
}
|
||||
let mut success = true;
|
||||
let r = std::process::Command::new("/bin/groupadd")
|
||||
.arg("-g")
|
||||
.arg(gid.clone())
|
||||
.arg(groupname)
|
||||
.status();
|
||||
if let Ok(exit_status) = r {
|
||||
if exit_status.code() != Some(0) {
|
||||
success = false;
|
||||
}
|
||||
} else {
|
||||
success = false;
|
||||
}
|
||||
|
||||
if !success {
|
||||
ErrorHandler::error_handle("groupadd failed".to_string(), ExitStatus::GroupaddFail);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// userdel命令检查器
|
||||
#[derive(Debug)]
|
||||
pub struct UDelCheck;
|
||||
|
||||
impl UDelCheck {
|
||||
/// **校验userdel命令**
|
||||
///
|
||||
/// ## 参数
|
||||
/// - `cmd`: userdel命令
|
||||
///
|
||||
/// ## 返回
|
||||
/// - `UDelInfo`: 校验后的用户信息
|
||||
pub fn check(cmd: UserCommand) -> UDelInfo {
|
||||
let mut info = UDelInfo::default();
|
||||
info.username = cmd.username;
|
||||
|
||||
// 检查用户是否存在
|
||||
scan_passwd(
|
||||
PasswdField {
|
||||
username: Some(info.username.clone()),
|
||||
uid: None,
|
||||
},
|
||||
true,
|
||||
);
|
||||
|
||||
if let Some(_) = cmd.options.get(&CmdOption::Remove) {
|
||||
info.home = Some(Self::home(&info.username));
|
||||
}
|
||||
|
||||
info
|
||||
}
|
||||
|
||||
/// 获取用户家目录
|
||||
fn home(username: &String) -> String {
|
||||
let mut home = String::new();
|
||||
match std::fs::read_to_string("/etc/passwd") {
|
||||
Ok(data) => {
|
||||
for line in data.lines() {
|
||||
let data = line.split(':').collect::<Vec<&str>>();
|
||||
if data[0] == username {
|
||||
home = data[5].to_string();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
ErrorHandler::error_handle(
|
||||
"Can't read file: /etc/passwd".to_string(),
|
||||
ExitStatus::PasswdFile,
|
||||
);
|
||||
}
|
||||
}
|
||||
home
|
||||
}
|
||||
}
|
||||
|
||||
/// usermod命令检查器
|
||||
#[derive(Debug)]
|
||||
pub struct UModCheck;
|
||||
|
||||
impl UModCheck {
|
||||
/// **校验usermod命令**
|
||||
///
|
||||
/// ## 参数
|
||||
/// - `cmd`: usermod命令
|
||||
///
|
||||
/// ## 返回
|
||||
/// - `UModInfo`: 校验后的用户信息
|
||||
pub fn check(cmd: UserCommand) -> UModInfo {
|
||||
let mut info = Self::parse_options(&cmd.options);
|
||||
info.username = cmd.username;
|
||||
|
||||
// 校验shell是否有效
|
||||
if let Some(shell) = &info.new_shell {
|
||||
check_shell(shell);
|
||||
}
|
||||
|
||||
// 校验new_home是否有效
|
||||
if let Some(new_home) = &info.new_home {
|
||||
Self::check_home(new_home);
|
||||
}
|
||||
|
||||
// 校验用户是否存在
|
||||
scan_passwd(
|
||||
PasswdField {
|
||||
username: Some(info.username.clone()),
|
||||
uid: None,
|
||||
},
|
||||
true,
|
||||
);
|
||||
|
||||
// 校验new_name、new_uid是否有效
|
||||
scan_passwd(
|
||||
PasswdField {
|
||||
username: info.new_name.clone(),
|
||||
uid: info.new_uid.clone(),
|
||||
},
|
||||
false,
|
||||
);
|
||||
|
||||
// 校验groups、new_gid是否有效
|
||||
scan_group(
|
||||
GroupField {
|
||||
groups: info.groups.clone(),
|
||||
gid: info.new_gid.clone(),
|
||||
},
|
||||
true,
|
||||
);
|
||||
|
||||
info
|
||||
}
|
||||
|
||||
/// **校验home目录是否有效**
|
||||
///
|
||||
/// ## 参数
|
||||
/// - `home`: home目录路径
|
||||
fn check_home(home: &String) {
|
||||
if fs::File::open(home).is_ok() {
|
||||
ErrorHandler::error_handle(format!("{} already exists", home), ExitStatus::InvalidArg);
|
||||
}
|
||||
}
|
||||
|
||||
/// **解析options**
|
||||
///
|
||||
/// ## 参数
|
||||
/// - `options`: 命令选项
|
||||
///
|
||||
/// ## 返回
|
||||
/// - `UModInfo`: 用户信息
|
||||
fn parse_options(options: &HashMap<CmdOption, String>) -> UModInfo {
|
||||
let mut info = UModInfo::default();
|
||||
for (option, arg) in options {
|
||||
match option {
|
||||
CmdOption::Append => {
|
||||
info.groups = Some(arg.split(",").map(|s| s.to_string()).collect());
|
||||
}
|
||||
CmdOption::Comment => {
|
||||
info.new_comment = Some(arg.clone());
|
||||
}
|
||||
CmdOption::Dir => {
|
||||
info.new_home = Some(arg.clone());
|
||||
}
|
||||
CmdOption::Gid => {
|
||||
info.new_gid = Some(arg.clone());
|
||||
}
|
||||
CmdOption::Login => {
|
||||
info.new_name = Some(arg.clone());
|
||||
}
|
||||
CmdOption::Shell => {
|
||||
info.new_shell = Some(arg.clone());
|
||||
}
|
||||
CmdOption::Uid => {
|
||||
info.new_uid = Some(arg.clone());
|
||||
}
|
||||
_ => ErrorHandler::error_handle(
|
||||
"Invalid option".to_string(),
|
||||
ExitStatus::InvalidCmdSyntax,
|
||||
),
|
||||
}
|
||||
}
|
||||
info
|
||||
}
|
||||
}
|
||||
|
||||
/// passwd命令检查器
|
||||
#[derive(Debug)]
|
||||
pub struct PasswdCheck;
|
||||
|
||||
impl PasswdCheck {
|
||||
/// **校验passwd命令**
|
||||
///
|
||||
/// ## 参数
|
||||
/// - `cmd`: passwd命令
|
||||
///
|
||||
/// ## 返回
|
||||
/// - `PasswdInfo`: 校验后的信息
|
||||
pub fn check(cmd: PasswdCommand) -> PasswdInfo {
|
||||
let uid = unsafe { libc::geteuid().to_string() };
|
||||
let cur_username = Self::cur_username(uid.clone());
|
||||
let mut to_change_username = String::new();
|
||||
|
||||
if let Some(username) = cmd.username {
|
||||
to_change_username = username.clone();
|
||||
|
||||
// 不是root用户不能修改别人的密码
|
||||
if uid != "0" && cur_username != username {
|
||||
ErrorHandler::error_handle(
|
||||
"You can't change password for other users".to_string(),
|
||||
ExitStatus::PermissionDenied,
|
||||
);
|
||||
}
|
||||
|
||||
// 检验待修改用户是否存在
|
||||
scan_passwd(
|
||||
PasswdField {
|
||||
username: Some(username.clone()),
|
||||
uid: None,
|
||||
},
|
||||
true,
|
||||
);
|
||||
}
|
||||
|
||||
let mut new_password = String::new();
|
||||
match uid.as_str() {
|
||||
"0" => {
|
||||
if to_change_username.is_empty() {
|
||||
to_change_username = cur_username;
|
||||
}
|
||||
print!("New password: ");
|
||||
std::io::stdout().flush().unwrap();
|
||||
std::io::stdin().read_line(&mut new_password).unwrap();
|
||||
new_password = new_password.trim().to_string();
|
||||
let mut check_password = String::new();
|
||||
print!("\nRe-enter new password: ");
|
||||
std::io::stdout().flush().unwrap();
|
||||
std::io::stdin().read_line(&mut check_password).unwrap();
|
||||
check_password = check_password.trim().to_string();
|
||||
if new_password != check_password {
|
||||
ErrorHandler::error_handle(
|
||||
"\nThe two passwords that you entered do not match.".to_string(),
|
||||
ExitStatus::InvalidArg,
|
||||
)
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
to_change_username = cur_username.clone();
|
||||
print!("Old password: ");
|
||||
std::io::stdout().flush().unwrap();
|
||||
let mut old_password = String::new();
|
||||
std::io::stdin().read_line(&mut old_password).unwrap();
|
||||
old_password = old_password.trim().to_string();
|
||||
Self::check_password(cur_username, old_password);
|
||||
print!("\nNew password: ");
|
||||
std::io::stdout().flush().unwrap();
|
||||
std::io::stdin().read_line(&mut new_password).unwrap();
|
||||
new_password = new_password.trim().to_string();
|
||||
print!("\nRe-enter new password: ");
|
||||
std::io::stdout().flush().unwrap();
|
||||
let mut check_password = String::new();
|
||||
std::io::stdin().read_line(&mut check_password).unwrap();
|
||||
check_password = check_password.trim().to_string();
|
||||
if new_password != check_password {
|
||||
println!("{}", new_password);
|
||||
ErrorHandler::error_handle(
|
||||
"\nThe two passwords that you entered do not match.".to_string(),
|
||||
ExitStatus::InvalidArg,
|
||||
)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
PasswdInfo {
|
||||
username: to_change_username,
|
||||
new_password,
|
||||
}
|
||||
}
|
||||
|
||||
/// **获取uid对应的用户名**
|
||||
///
|
||||
/// ## 参数
|
||||
/// - `uid`: 用户id
|
||||
///
|
||||
/// ## 返回
|
||||
/// 用户名
|
||||
fn cur_username(uid: String) -> String {
|
||||
let r = fs::read_to_string("/etc/passwd");
|
||||
let mut cur_username = String::new();
|
||||
|
||||
match r {
|
||||
Ok(content) => {
|
||||
for line in content.lines() {
|
||||
let field = line.split(":").collect::<Vec<&str>>();
|
||||
if uid == field[2] {
|
||||
cur_username = field[0].to_string();
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
ErrorHandler::error_handle(
|
||||
"Can't read /etc/passwd".to_string(),
|
||||
ExitStatus::PasswdFile,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
cur_username
|
||||
}
|
||||
|
||||
/// **校验密码**
|
||||
///
|
||||
/// ## 参数
|
||||
/// - `username`: 用户名
|
||||
/// - `password`: 密码
|
||||
fn check_password(username: String, password: String) {
|
||||
let r = fs::read_to_string("/etc/shadow");
|
||||
match r {
|
||||
Ok(content) => {
|
||||
for line in content.lines() {
|
||||
let field = line.split(":").collect::<Vec<&str>>();
|
||||
if username == field[0] {
|
||||
if password != field[1] {
|
||||
ErrorHandler::error_handle(
|
||||
"Password error".to_string(),
|
||||
ExitStatus::InvalidArg,
|
||||
);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
ErrorHandler::error_handle(
|
||||
"Can't read /etc/shadow".to_string(),
|
||||
ExitStatus::ShadowFile,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// groupadd命令检查器
|
||||
#[derive(Debug)]
|
||||
pub struct GAddCheck;
|
||||
|
||||
impl GAddCheck {
|
||||
/// **校验groupadd命令**
|
||||
///
|
||||
/// ## 参数
|
||||
/// - `cmd`: groupadd命令
|
||||
///
|
||||
/// ## 返回
|
||||
/// - `GAddInfo`: 校验后的组信息
|
||||
pub fn check(cmd: GroupCommand) -> GAddInfo {
|
||||
let mut info = GAddInfo {
|
||||
groupname: cmd.groupname.clone(),
|
||||
gid: String::new(),
|
||||
passwd: None,
|
||||
};
|
||||
|
||||
if info.groupname.is_empty() {
|
||||
ErrorHandler::error_handle("groupname is required".to_string(), ExitStatus::InvalidArg);
|
||||
}
|
||||
|
||||
if let Some(gid) = cmd.options.get(&CmdOption::Gid) {
|
||||
info.gid = gid.clone();
|
||||
} else {
|
||||
ErrorHandler::error_handle("gid is required".to_string(), ExitStatus::InvalidArg);
|
||||
}
|
||||
|
||||
if let Some(passwd) = cmd.options.get(&CmdOption::Passwd) {
|
||||
info.passwd = Some(passwd.clone());
|
||||
}
|
||||
|
||||
// 检查组名或组id是否已存在
|
||||
scan_group(
|
||||
GroupField {
|
||||
groups: Some(vec![info.groupname.clone()]),
|
||||
gid: Some(info.gid.clone()),
|
||||
},
|
||||
false,
|
||||
);
|
||||
|
||||
info
|
||||
}
|
||||
}
|
||||
|
||||
/// groupdel命令检查器
|
||||
#[derive(Debug)]
|
||||
pub struct GDelCheck;
|
||||
|
||||
impl GDelCheck {
|
||||
/// **校验groupdel命令**
|
||||
///
|
||||
/// ## 参数
|
||||
/// - `cmd`: groupdel命令
|
||||
///
|
||||
/// ## 返回
|
||||
/// - `GDelInfo`: 校验后的组信息
|
||||
pub fn check(cmd: GroupCommand) -> GDelInfo {
|
||||
if let Some(gid) = check_groupname(cmd.groupname.clone()) {
|
||||
// 检查group是不是某个用户的主组,如果是的话则不能删除
|
||||
Self::is_main_group(gid);
|
||||
} else {
|
||||
// 用户组不存在
|
||||
ErrorHandler::error_handle(
|
||||
format!("group:[{}] doesn't exist", cmd.groupname),
|
||||
ExitStatus::GroupNotExist,
|
||||
);
|
||||
}
|
||||
GDelInfo {
|
||||
groupname: cmd.groupname,
|
||||
}
|
||||
}
|
||||
|
||||
/// **检查该组是否为某个用户的主用户组**
|
||||
///
|
||||
/// ## 参数
|
||||
/// - `gid`: 组id
|
||||
///
|
||||
/// ## 返回
|
||||
/// Some(gid): 组id
|
||||
/// None
|
||||
fn is_main_group(gid: String) {
|
||||
// 读取/etc/passwd文件
|
||||
let r = fs::read_to_string("/etc/passwd");
|
||||
match r {
|
||||
Ok(content) => {
|
||||
for line in content.lines() {
|
||||
let field = line.split(":").collect::<Vec<&str>>();
|
||||
if field[3] == gid {
|
||||
ErrorHandler::error_handle(
|
||||
format!(
|
||||
"groupdel failed: group is main group of user:[{}]",
|
||||
field[0]
|
||||
),
|
||||
ExitStatus::InvalidArg,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
ErrorHandler::error_handle(
|
||||
"Can't read file: /etc/passwd".to_string(),
|
||||
ExitStatus::PasswdFile,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// groupmod命令检查器
|
||||
#[derive(Debug)]
|
||||
pub struct GModCheck;
|
||||
|
||||
impl GModCheck {
|
||||
/// **校验groupmod命令**
|
||||
///
|
||||
/// ## 参数
|
||||
/// - `cmd`: groupmod命令
|
||||
///
|
||||
/// ## 返回
|
||||
/// - `GModInfo`: 校验后的组信息
|
||||
pub fn check(cmd: GroupCommand) -> GModInfo {
|
||||
let mut info = GModInfo::default();
|
||||
info.groupname = cmd.groupname;
|
||||
|
||||
if let Some(new_groupname) = cmd.options.get(&CmdOption::NewGroupName) {
|
||||
info.new_groupname = Some(new_groupname.clone());
|
||||
}
|
||||
|
||||
if let Some(new_gid) = cmd.options.get(&CmdOption::Gid) {
|
||||
info.new_gid = Some(new_gid.clone());
|
||||
}
|
||||
|
||||
Self::check_group_file(&mut info);
|
||||
|
||||
info
|
||||
}
|
||||
|
||||
/// 查看groupname是否存在,同时检测new_gid、new_groupname是否重复
|
||||
fn check_group_file(info: &mut GModInfo) {
|
||||
let mut is_group_exist = false;
|
||||
let r = fs::read_to_string("/etc/group");
|
||||
match r {
|
||||
Ok(content) => {
|
||||
for line in content.lines() {
|
||||
let field = line.split(':').collect::<Vec<&str>>();
|
||||
if field[0] == info.groupname {
|
||||
is_group_exist = true;
|
||||
info.gid = field[2].to_string();
|
||||
}
|
||||
|
||||
if let Some(new_gid) = &info.new_gid {
|
||||
if new_gid == field[2] {
|
||||
ErrorHandler::error_handle(
|
||||
format!("gid:[{}] is already used", new_gid),
|
||||
ExitStatus::InvalidArg,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(new_groupname) = &info.new_groupname {
|
||||
if new_groupname == field[0] {
|
||||
ErrorHandler::error_handle(
|
||||
format!("groupname:[{}] is already used", new_groupname),
|
||||
ExitStatus::InvalidArg,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(_) => ErrorHandler::error_handle(
|
||||
"Can't read file: /etc/group".to_string(),
|
||||
ExitStatus::GroupFile,
|
||||
),
|
||||
}
|
||||
|
||||
if !is_group_exist {
|
||||
ErrorHandler::error_handle(
|
||||
format!("groupname:[{}] doesn't exist", info.groupname),
|
||||
ExitStatus::GroupNotExist,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// passwd文件待校验字段
|
||||
pub struct PasswdField {
|
||||
username: Option<String>,
|
||||
uid: Option<String>,
|
||||
}
|
||||
|
||||
/// group文件待校验字段
|
||||
pub struct GroupField {
|
||||
groups: Option<Vec<String>>,
|
||||
gid: Option<String>,
|
||||
}
|
||||
|
||||
/// **校验uid**
|
||||
///
|
||||
/// ## 参数
|
||||
/// - `passwd_field`: passwd文件字段
|
||||
/// - `should_exist`: 是否应该存在
|
||||
fn scan_passwd(passwd_field: PasswdField, should_exist: bool) {
|
||||
let mut username_check = false;
|
||||
let mut uid_check = false;
|
||||
match fs::read_to_string("/etc/passwd") {
|
||||
Ok(content) => {
|
||||
for line in content.lines() {
|
||||
let field = line.split(':').collect::<Vec<&str>>();
|
||||
if let Some(uid) = &passwd_field.uid {
|
||||
// uid必须是有效的数字
|
||||
let r = uid.parse::<u32>();
|
||||
if r.is_err() {
|
||||
ErrorHandler::error_handle(
|
||||
format!("Uid {} is invalid", uid),
|
||||
ExitStatus::InvalidArg,
|
||||
);
|
||||
}
|
||||
if field[2] == uid {
|
||||
uid_check = true;
|
||||
// username如果不用校验或者被校验过了,才可以return
|
||||
if should_exist && (passwd_field.username.is_none() || username_check) {
|
||||
return;
|
||||
} else {
|
||||
ErrorHandler::error_handle(
|
||||
format!("UID {} already exists", uid),
|
||||
ExitStatus::UidInUse,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(username) = &passwd_field.username {
|
||||
if field[0] == username {
|
||||
username_check = true;
|
||||
// uid如果不用校验或者被校验过了,才可以return
|
||||
if should_exist && (passwd_field.uid.is_none() || uid_check) {
|
||||
return;
|
||||
} else {
|
||||
ErrorHandler::error_handle(
|
||||
format!("Username {} already exists", username),
|
||||
ExitStatus::UsernameInUse,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if should_exist {
|
||||
if let Some(uid) = &passwd_field.uid {
|
||||
if !uid_check {
|
||||
ErrorHandler::error_handle(
|
||||
format!("UID {} doesn't exist", uid),
|
||||
ExitStatus::InvalidArg,
|
||||
);
|
||||
}
|
||||
}
|
||||
if let Some(username) = &passwd_field.username {
|
||||
if !username_check {
|
||||
ErrorHandler::error_handle(
|
||||
format!("User {} doesn't exist", username),
|
||||
ExitStatus::InvalidArg,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(_) => ErrorHandler::error_handle(
|
||||
"Can't read file: /etc/passwd".to_string(),
|
||||
ExitStatus::PasswdFile,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
/// **校验gid**
|
||||
///
|
||||
/// ## 参数
|
||||
/// - `group_field`: group文件字段
|
||||
/// - `should_exist`: 是否应该存在
|
||||
fn scan_group(group_field: GroupField, should_exist: bool) {
|
||||
let mut gid_check = false;
|
||||
let mut set1 = HashSet::new();
|
||||
let mut set2 = HashSet::new();
|
||||
if let Some(groups) = group_field.groups.clone() {
|
||||
set2.extend(groups.into_iter());
|
||||
}
|
||||
match fs::read_to_string("/etc/group") {
|
||||
Ok(content) => {
|
||||
for line in content.lines() {
|
||||
let field = line.split(':').collect::<Vec<&str>>();
|
||||
if let Some(gid) = &group_field.gid {
|
||||
// gid必须是有效的数字
|
||||
let r = gid.parse::<u32>();
|
||||
if r.is_err() {
|
||||
ErrorHandler::error_handle(
|
||||
format!("Gid {} is invalid", gid),
|
||||
ExitStatus::InvalidArg,
|
||||
);
|
||||
}
|
||||
if field[2] == gid {
|
||||
gid_check = true;
|
||||
if should_exist && group_field.groups.is_none() {
|
||||
return;
|
||||
} else {
|
||||
ErrorHandler::error_handle(
|
||||
format!("GID {} already exists", gid),
|
||||
ExitStatus::InvalidArg,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 统计所有组
|
||||
set1.insert(field[0].to_string());
|
||||
}
|
||||
|
||||
if should_exist {
|
||||
if let Some(gid) = &group_field.gid {
|
||||
if !gid_check {
|
||||
ErrorHandler::error_handle(
|
||||
format!("GID {} doesn't exist", gid),
|
||||
ExitStatus::InvalidArg,
|
||||
);
|
||||
}
|
||||
}
|
||||
if group_field.groups.is_some() {
|
||||
let mut non_exist_group = Vec::new();
|
||||
for group in set2.iter() {
|
||||
if !set1.contains(group) {
|
||||
non_exist_group.push(group.clone());
|
||||
}
|
||||
}
|
||||
|
||||
if non_exist_group.len() > 0 {
|
||||
ErrorHandler::error_handle(
|
||||
format!("group: {} doesn't exist", non_exist_group.join(",")),
|
||||
ExitStatus::GroupNotExist,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Err(_) => ErrorHandler::error_handle(
|
||||
"Can't read file: /etc/group".to_string(),
|
||||
ExitStatus::GroupFile,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
/// **校验shell是否有效**
|
||||
///
|
||||
/// ## 参数
|
||||
/// - `shell`: shell路径
|
||||
fn check_shell(shell: &String) {
|
||||
if let Ok(file) = fs::File::open(shell.clone()) {
|
||||
if !file.metadata().unwrap().is_file() {
|
||||
ErrorHandler::error_handle(format!("{} is not a file", shell), ExitStatus::InvalidArg);
|
||||
}
|
||||
} else {
|
||||
ErrorHandler::error_handle(format!("{} doesn't exist", shell), ExitStatus::InvalidArg);
|
||||
}
|
||||
}
|
||||
|
||||
/// **校验组名,判断该用户组是否存在,以及成员是否为空**
|
||||
///
|
||||
/// ## 参数
|
||||
/// - `groupname`: 组名
|
||||
///
|
||||
/// ## 返回
|
||||
/// Some(gid): 组id
|
||||
/// None
|
||||
fn check_groupname(groupname: String) -> Option<String> {
|
||||
let r = fs::read_to_string("/etc/group");
|
||||
match r {
|
||||
Ok(content) => {
|
||||
for line in content.lines() {
|
||||
let field = line.split(":").collect::<Vec<&str>>();
|
||||
let users = field[3].split(",").collect::<Vec<&str>>();
|
||||
let filter_users = users
|
||||
.iter()
|
||||
.filter(|&x| !x.is_empty())
|
||||
.collect::<Vec<&&str>>();
|
||||
if field[0] == groupname {
|
||||
if filter_users.is_empty() {
|
||||
return Some(field[2].to_string());
|
||||
} else {
|
||||
ErrorHandler::error_handle(
|
||||
format!("group:[{}] is not empty, unable to delete", groupname),
|
||||
ExitStatus::InvalidArg,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
ErrorHandler::error_handle(
|
||||
"Can't read file: /etc/group".to_string(),
|
||||
ExitStatus::GroupFile,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
95
user/apps/user-manage/src/check/info.rs
Normal file
95
user/apps/user-manage/src/check/info.rs
Normal file
@ -0,0 +1,95 @@
|
||||
#[derive(Debug, Default, Clone)]
|
||||
/// useradd的信息
|
||||
pub struct UAddInfo {
|
||||
/// 用户名
|
||||
pub username: String,
|
||||
pub uid: String,
|
||||
pub gid: String,
|
||||
/// 所在组的组名
|
||||
pub group: String,
|
||||
/// 用户描述信息
|
||||
pub comment: String,
|
||||
/// 主目录
|
||||
pub home_dir: String,
|
||||
/// 终端程序名
|
||||
pub shell: String,
|
||||
}
|
||||
|
||||
impl From<UAddInfo> for String {
|
||||
fn from(info: UAddInfo) -> Self {
|
||||
format!(
|
||||
"{}::{}:{}:{}:{}:{}\n",
|
||||
info.username, info.uid, info.gid, info.comment, info.home_dir, info.shell
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
/// userdel的信息
|
||||
pub struct UDelInfo {
|
||||
pub username: String,
|
||||
pub home: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
/// usermod的信息
|
||||
pub struct UModInfo {
|
||||
pub username: String,
|
||||
pub groups: Option<Vec<String>>,
|
||||
pub new_comment: Option<String>,
|
||||
pub new_home: Option<String>,
|
||||
pub new_gid: Option<String>,
|
||||
pub new_group: Option<String>,
|
||||
pub new_name: Option<String>,
|
||||
pub new_shell: Option<String>,
|
||||
pub new_uid: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
/// passwd的信息
|
||||
pub struct PasswdInfo {
|
||||
pub username: String,
|
||||
pub new_password: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
/// groupadd的信息
|
||||
pub struct GAddInfo {
|
||||
pub groupname: String,
|
||||
pub gid: String,
|
||||
pub passwd: Option<String>,
|
||||
}
|
||||
|
||||
impl GAddInfo {
|
||||
pub fn to_string_group(&self) -> String {
|
||||
let mut passwd = String::from("");
|
||||
if self.passwd.is_some() {
|
||||
passwd = "x".to_string();
|
||||
}
|
||||
format!("{}:{}:{}:\n", self.groupname, passwd, self.gid)
|
||||
}
|
||||
|
||||
pub fn to_string_gshadow(&self) -> String {
|
||||
let mut passwd = String::from("!");
|
||||
if let Some(gpasswd) = &self.passwd {
|
||||
passwd = gpasswd.clone();
|
||||
}
|
||||
|
||||
format!("{}:{}::\n", self.groupname, passwd)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
/// groupdel的信息
|
||||
pub struct GDelInfo {
|
||||
pub groupname: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
/// groupmod的信息
|
||||
pub struct GModInfo {
|
||||
pub groupname: String,
|
||||
pub gid: String,
|
||||
pub new_groupname: Option<String>,
|
||||
pub new_gid: Option<String>,
|
||||
}
|
3
user/apps/user-manage/src/check/mod.rs
Normal file
3
user/apps/user-manage/src/check/mod.rs
Normal file
@ -0,0 +1,3 @@
|
||||
#![allow(dead_code)]
|
||||
pub mod check;
|
||||
pub mod info;
|
45
user/apps/user-manage/src/cmd/groupadd.rs
Normal file
45
user/apps/user-manage/src/cmd/groupadd.rs
Normal file
@ -0,0 +1,45 @@
|
||||
use crate::{
|
||||
check::check::GAddCheck,
|
||||
error::error::{ErrorHandler, ExitStatus},
|
||||
executor::executor::GAddExecutor,
|
||||
parser::parser::GroupParser,
|
||||
};
|
||||
use libc::geteuid;
|
||||
use std::process::exit;
|
||||
|
||||
#[path = "../check/mod.rs"]
|
||||
mod check;
|
||||
#[path = "../error/mod.rs"]
|
||||
mod error;
|
||||
#[path = "../executor/mod.rs"]
|
||||
mod executor;
|
||||
#[path = "../parser/mod.rs"]
|
||||
mod parser;
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn main() {
|
||||
let args = std::env::args().collect::<Vec<_>>();
|
||||
|
||||
if unsafe { geteuid() } != 0 {
|
||||
ErrorHandler::error_handle(
|
||||
"permission denied (are you root?)".to_string(),
|
||||
ExitStatus::PermissionDenied,
|
||||
)
|
||||
}
|
||||
|
||||
if args.len() < 2 {
|
||||
ErrorHandler::error_handle(
|
||||
format!("usage: {} [options] groupname", args[0]),
|
||||
ExitStatus::InvalidCmdSyntax,
|
||||
);
|
||||
}
|
||||
|
||||
let cmd = GroupParser::parse(args);
|
||||
let info = GAddCheck::check(cmd);
|
||||
let groupname = info.groupname.clone();
|
||||
GAddExecutor::execute(info);
|
||||
|
||||
println!("Add group [{}] successfully!", groupname);
|
||||
|
||||
exit(ExitStatus::Success as i32);
|
||||
}
|
45
user/apps/user-manage/src/cmd/groupdel.rs
Normal file
45
user/apps/user-manage/src/cmd/groupdel.rs
Normal file
@ -0,0 +1,45 @@
|
||||
use crate::{
|
||||
check::check::GDelCheck,
|
||||
error::error::{ErrorHandler, ExitStatus},
|
||||
executor::executor::GDelExecutor,
|
||||
parser::parser::GroupParser,
|
||||
};
|
||||
use libc::geteuid;
|
||||
use std::process::exit;
|
||||
|
||||
#[path = "../check/mod.rs"]
|
||||
mod check;
|
||||
#[path = "../error/mod.rs"]
|
||||
mod error;
|
||||
#[path = "../executor/mod.rs"]
|
||||
mod executor;
|
||||
#[path = "../parser/mod.rs"]
|
||||
mod parser;
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn main() {
|
||||
let args = std::env::args().collect::<Vec<_>>();
|
||||
|
||||
if unsafe { geteuid() } != 0 {
|
||||
ErrorHandler::error_handle(
|
||||
"permission denied (are you root?)".to_string(),
|
||||
ExitStatus::PermissionDenied,
|
||||
)
|
||||
}
|
||||
|
||||
if args.len() < 2 {
|
||||
ErrorHandler::error_handle(
|
||||
format!("usage: {} [options] groupname", args[0]),
|
||||
ExitStatus::InvalidCmdSyntax,
|
||||
);
|
||||
}
|
||||
|
||||
let cmd = GroupParser::parse(args);
|
||||
let info = GDelCheck::check(cmd);
|
||||
let groupname = info.groupname.clone();
|
||||
GDelExecutor::execute(info);
|
||||
|
||||
println!("Delete group [{}] successfully!", groupname);
|
||||
|
||||
exit(ExitStatus::Success as i32);
|
||||
}
|
46
user/apps/user-manage/src/cmd/groupmod.rs
Normal file
46
user/apps/user-manage/src/cmd/groupmod.rs
Normal file
@ -0,0 +1,46 @@
|
||||
use crate::{
|
||||
check::check::GModCheck,
|
||||
error::error::{ErrorHandler, ExitStatus},
|
||||
executor::executor::GModExecutor,
|
||||
parser::parser::GroupParser,
|
||||
};
|
||||
use libc::geteuid;
|
||||
use std::process::exit;
|
||||
|
||||
#[path = "../check/mod.rs"]
|
||||
mod check;
|
||||
#[path = "../error/mod.rs"]
|
||||
mod error;
|
||||
#[path = "../executor/mod.rs"]
|
||||
mod executor;
|
||||
#[path = "../parser/mod.rs"]
|
||||
mod parser;
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn main() {
|
||||
let args = std::env::args().collect::<Vec<_>>();
|
||||
|
||||
if unsafe { geteuid() } != 0 {
|
||||
ErrorHandler::error_handle(
|
||||
"permission denied (are you root?)".to_string(),
|
||||
ExitStatus::PermissionDenied,
|
||||
)
|
||||
}
|
||||
|
||||
if args.len() < 2 {
|
||||
ErrorHandler::error_handle(
|
||||
format!("usage: {} [options] groupname", args[0]),
|
||||
ExitStatus::InvalidCmdSyntax,
|
||||
);
|
||||
}
|
||||
|
||||
let cmd = GroupParser::parse(args);
|
||||
if !cmd.options.is_empty() {
|
||||
let info = GModCheck::check(cmd);
|
||||
let groupname = info.groupname.clone();
|
||||
GModExecutor::execute(info);
|
||||
println!("Modify group [{}] successfully!", groupname);
|
||||
}
|
||||
|
||||
exit(ExitStatus::Success as i32);
|
||||
}
|
7
user/apps/user-manage/src/cmd/mod.rs
Normal file
7
user/apps/user-manage/src/cmd/mod.rs
Normal file
@ -0,0 +1,7 @@
|
||||
mod groupadd;
|
||||
mod groupdel;
|
||||
mod groupmod;
|
||||
mod passwd;
|
||||
mod useradd;
|
||||
mod userdel;
|
||||
mod usermod;
|
25
user/apps/user-manage/src/cmd/passwd.rs
Normal file
25
user/apps/user-manage/src/cmd/passwd.rs
Normal file
@ -0,0 +1,25 @@
|
||||
use crate::{
|
||||
check::check::PasswdCheck, error::error::ExitStatus, executor::executor::PasswdExecutor,
|
||||
parser::parser::PasswdParser,
|
||||
};
|
||||
use std::process::exit;
|
||||
|
||||
#[path = "../check/mod.rs"]
|
||||
mod check;
|
||||
#[path = "../error/mod.rs"]
|
||||
mod error;
|
||||
#[path = "../executor/mod.rs"]
|
||||
mod executor;
|
||||
#[path = "../parser/mod.rs"]
|
||||
mod parser;
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn main() {
|
||||
let args = std::env::args().collect::<Vec<_>>();
|
||||
|
||||
let cmd = PasswdParser::parse(args);
|
||||
let info = PasswdCheck::check(cmd);
|
||||
PasswdExecutor::execute(info);
|
||||
|
||||
exit(ExitStatus::Success as i32);
|
||||
}
|
44
user/apps/user-manage/src/cmd/useradd.rs
Normal file
44
user/apps/user-manage/src/cmd/useradd.rs
Normal file
@ -0,0 +1,44 @@
|
||||
use crate::{
|
||||
check::check::UAddCheck,
|
||||
error::error::{ErrorHandler, ExitStatus},
|
||||
executor::executor::UAddExecutor,
|
||||
parser::parser::UserParser,
|
||||
};
|
||||
use libc::geteuid;
|
||||
use std::process::exit;
|
||||
|
||||
#[path = "../check/mod.rs"]
|
||||
mod check;
|
||||
#[path = "../error/mod.rs"]
|
||||
mod error;
|
||||
#[path = "../executor/mod.rs"]
|
||||
mod executor;
|
||||
#[path = "../parser/mod.rs"]
|
||||
mod parser;
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn main() {
|
||||
let args = std::env::args().collect::<Vec<_>>();
|
||||
|
||||
if unsafe { geteuid() } != 0 {
|
||||
ErrorHandler::error_handle(
|
||||
"permission denied (are you root?)".to_string(),
|
||||
ExitStatus::PermissionDenied,
|
||||
)
|
||||
}
|
||||
|
||||
if args.len() < 2 {
|
||||
ErrorHandler::error_handle(
|
||||
format!("usage: {} [options] username", args[0]),
|
||||
ExitStatus::InvalidCmdSyntax,
|
||||
);
|
||||
}
|
||||
|
||||
let cmd = UserParser::parse(args);
|
||||
let info = UAddCheck::check(cmd);
|
||||
let username = info.username.clone();
|
||||
UAddExecutor::execute(info);
|
||||
println!("Add user[{}] successfully!", username);
|
||||
|
||||
exit(ExitStatus::Success as i32);
|
||||
}
|
44
user/apps/user-manage/src/cmd/userdel.rs
Normal file
44
user/apps/user-manage/src/cmd/userdel.rs
Normal file
@ -0,0 +1,44 @@
|
||||
use crate::{
|
||||
check::check::UDelCheck,
|
||||
error::error::{ErrorHandler, ExitStatus},
|
||||
executor::executor::UDelExecutor,
|
||||
parser::parser::UserParser,
|
||||
};
|
||||
use libc::geteuid;
|
||||
use std::process::exit;
|
||||
|
||||
#[path = "../check/mod.rs"]
|
||||
mod check;
|
||||
#[path = "../error/mod.rs"]
|
||||
mod error;
|
||||
#[path = "../executor/mod.rs"]
|
||||
mod executor;
|
||||
#[path = "../parser/mod.rs"]
|
||||
mod parser;
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn main() {
|
||||
let args = std::env::args().collect::<Vec<_>>();
|
||||
|
||||
if unsafe { geteuid() } != 0 {
|
||||
ErrorHandler::error_handle(
|
||||
"permission denied (are you root?)".to_string(),
|
||||
ExitStatus::PermissionDenied,
|
||||
)
|
||||
}
|
||||
|
||||
if args.len() < 2 {
|
||||
ErrorHandler::error_handle(
|
||||
format!("usage: {} [options] username", args[0]),
|
||||
ExitStatus::InvalidCmdSyntax,
|
||||
);
|
||||
}
|
||||
|
||||
let cmd = UserParser::parse(args);
|
||||
let info = UDelCheck::check(cmd);
|
||||
let username = info.username.clone();
|
||||
UDelExecutor::execute(info);
|
||||
println!("Delete user[{}] successfully!", username);
|
||||
|
||||
exit(ExitStatus::Success as i32);
|
||||
}
|
46
user/apps/user-manage/src/cmd/usermod.rs
Normal file
46
user/apps/user-manage/src/cmd/usermod.rs
Normal file
@ -0,0 +1,46 @@
|
||||
use crate::{
|
||||
check::check::UModCheck,
|
||||
error::error::{ErrorHandler, ExitStatus},
|
||||
executor::executor::UModExecutor,
|
||||
parser::parser::UserParser,
|
||||
};
|
||||
use libc::geteuid;
|
||||
use std::process::exit;
|
||||
|
||||
#[path = "../check/mod.rs"]
|
||||
mod check;
|
||||
#[path = "../error/mod.rs"]
|
||||
mod error;
|
||||
#[path = "../executor/mod.rs"]
|
||||
mod executor;
|
||||
#[path = "../parser/mod.rs"]
|
||||
mod parser;
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn main() {
|
||||
let args = std::env::args().collect::<Vec<_>>();
|
||||
|
||||
if unsafe { geteuid() } != 0 {
|
||||
ErrorHandler::error_handle(
|
||||
"permission denied (are you root?)".to_string(),
|
||||
ExitStatus::PermissionDenied,
|
||||
)
|
||||
}
|
||||
|
||||
if args.len() < 2 {
|
||||
ErrorHandler::error_handle(
|
||||
format!("usage: {} [options] username", args[0]),
|
||||
ExitStatus::InvalidCmdSyntax,
|
||||
);
|
||||
}
|
||||
|
||||
let cmd = UserParser::parse(args);
|
||||
if !cmd.options.is_empty() {
|
||||
let info = UModCheck::check(cmd);
|
||||
let username = info.username.clone();
|
||||
UModExecutor::execute(info);
|
||||
println!("Modify user[{}] successfully!", username);
|
||||
}
|
||||
|
||||
exit(ExitStatus::Success as i32);
|
||||
}
|
33
user/apps/user-manage/src/error/error.rs
Normal file
33
user/apps/user-manage/src/error/error.rs
Normal file
@ -0,0 +1,33 @@
|
||||
use std::process::exit;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ExitStatus {
|
||||
Success = 0,
|
||||
PasswdFile = 1,
|
||||
InvalidCmdSyntax = 2,
|
||||
InvalidArg = 3,
|
||||
UidInUse = 4,
|
||||
GroupNotExist = 6,
|
||||
UsernameInUse = 9,
|
||||
GroupFile = 10,
|
||||
CreateHomeFail = 12,
|
||||
PermissionDenied = -1,
|
||||
ShadowFile = -2,
|
||||
GshadowFile = -3,
|
||||
GroupaddFail = -4,
|
||||
}
|
||||
|
||||
pub struct ErrorHandler;
|
||||
|
||||
impl ErrorHandler {
|
||||
/// **错误处理函数**
|
||||
///
|
||||
/// ## 参数
|
||||
///
|
||||
/// - `error`错误信息
|
||||
/// - `exit_status` - 退出状态码
|
||||
pub fn error_handle(error: String, exit_status: ExitStatus) {
|
||||
eprintln!("{error}");
|
||||
exit(exit_status as i32);
|
||||
}
|
||||
}
|
2
user/apps/user-manage/src/error/mod.rs
Normal file
2
user/apps/user-manage/src/error/mod.rs
Normal file
@ -0,0 +1,2 @@
|
||||
#![allow(dead_code)]
|
||||
pub mod error;
|
729
user/apps/user-manage/src/executor/executor.rs
Normal file
729
user/apps/user-manage/src/executor/executor.rs
Normal file
@ -0,0 +1,729 @@
|
||||
use crate::{
|
||||
check::info::{GAddInfo, GDelInfo, GModInfo, PasswdInfo, UAddInfo, UDelInfo, UModInfo},
|
||||
error::error::{ErrorHandler, ExitStatus},
|
||||
};
|
||||
use lazy_static::lazy_static;
|
||||
use std::{
|
||||
fs::{self, File, OpenOptions},
|
||||
io::{Read, Seek, Write},
|
||||
sync::Mutex,
|
||||
};
|
||||
|
||||
lazy_static! {
|
||||
static ref GLOBAL_FILE: Mutex<GlobalFile> = Mutex::new(GlobalFile::new());
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct GlobalFile {
|
||||
passwd_file: File,
|
||||
shadow_file: File,
|
||||
group_file: File,
|
||||
gshadow_file: File,
|
||||
}
|
||||
|
||||
impl GlobalFile {
|
||||
pub fn new() -> Self {
|
||||
let passwd = open_file("/etc/passwd");
|
||||
let shadow = open_file("/etc/shadow");
|
||||
let group = open_file("/etc/group");
|
||||
let gshadow = open_file("/etc/gshadow");
|
||||
Self {
|
||||
passwd_file: passwd,
|
||||
shadow_file: shadow,
|
||||
group_file: group,
|
||||
gshadow_file: gshadow,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn open_file(file_path: &str) -> File {
|
||||
let r = OpenOptions::new()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.append(true)
|
||||
.open(file_path);
|
||||
|
||||
let exit_status = match file_path {
|
||||
"/etc/group" => ExitStatus::GroupFile,
|
||||
"/etc/gshadow" => ExitStatus::GshadowFile,
|
||||
"/etc/passwd" => ExitStatus::PasswdFile,
|
||||
"/etc/shadow" => ExitStatus::ShadowFile,
|
||||
_ => ExitStatus::InvalidArg,
|
||||
};
|
||||
|
||||
if r.is_err() {
|
||||
ErrorHandler::error_handle(format!("Can't open file: {}", file_path), exit_status);
|
||||
}
|
||||
|
||||
r.unwrap()
|
||||
}
|
||||
|
||||
/// useradd执行器
|
||||
pub struct UAddExecutor;
|
||||
|
||||
impl UAddExecutor {
|
||||
/// **执行useradd**
|
||||
///
|
||||
/// ## 参数
|
||||
/// - `info`: 用户信息
|
||||
pub fn execute(info: UAddInfo) {
|
||||
// 创建用户home目录
|
||||
let home = info.home_dir.clone();
|
||||
let dir_builder = fs::DirBuilder::new();
|
||||
if dir_builder.create(home.clone()).is_err() {
|
||||
ErrorHandler::error_handle(
|
||||
format!("unable to create {}", home),
|
||||
ExitStatus::CreateHomeFail,
|
||||
);
|
||||
}
|
||||
|
||||
Self::write_passwd_file(&info);
|
||||
Self::write_shadow_file(&info);
|
||||
Self::write_group_file(&info);
|
||||
Self::write_gshadow_file(&info);
|
||||
}
|
||||
|
||||
/// 写入/etc/passwd文件:添加用户信息
|
||||
fn write_passwd_file(info: &UAddInfo) {
|
||||
let userinfo: String = info.clone().into();
|
||||
GLOBAL_FILE
|
||||
.lock()
|
||||
.unwrap()
|
||||
.passwd_file
|
||||
.write_all(userinfo.as_bytes())
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
/// 写入/etc/group文件:将用户添加到对应用户组中
|
||||
fn write_group_file(info: &UAddInfo) {
|
||||
if info.group == info.username {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut guard = GLOBAL_FILE.lock().unwrap();
|
||||
let content = read_to_string(&guard.group_file);
|
||||
let mut new_content = String::new();
|
||||
for line in content.lines() {
|
||||
let mut field = line.split(":").collect::<Vec<&str>>();
|
||||
let mut users = field.last().unwrap().split(",").collect::<Vec<&str>>();
|
||||
users = users
|
||||
.into_iter()
|
||||
.filter(|username| !username.is_empty())
|
||||
.collect::<Vec<&str>>();
|
||||
if field[0].eq(info.group.as_str()) && !users.contains(&info.username.as_str()) {
|
||||
users.push(info.username.as_str());
|
||||
}
|
||||
|
||||
let new_users = users.join(",");
|
||||
field[3] = new_users.as_str();
|
||||
new_content.push_str(format!("{}\n", field.join(":")).as_str());
|
||||
}
|
||||
|
||||
guard.group_file.set_len(0).unwrap();
|
||||
guard.group_file.seek(std::io::SeekFrom::Start(0)).unwrap();
|
||||
guard.group_file.write_all(new_content.as_bytes()).unwrap();
|
||||
guard.group_file.flush().unwrap();
|
||||
}
|
||||
|
||||
/// 写入/etc/shadow文件:添加用户口令相关信息
|
||||
fn write_shadow_file(info: &UAddInfo) {
|
||||
let data = format!("{}::::::::\n", info.username,);
|
||||
GLOBAL_FILE
|
||||
.lock()
|
||||
.unwrap()
|
||||
.shadow_file
|
||||
.write_all(data.as_bytes())
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
/// 写入/etc/gshadow文件:将用户添加到对应用户组中
|
||||
fn write_gshadow_file(info: &UAddInfo) {
|
||||
if info.group == info.username {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut guard = GLOBAL_FILE.lock().unwrap();
|
||||
let content = read_to_string(&guard.gshadow_file);
|
||||
let mut new_content = String::new();
|
||||
for line in content.lines() {
|
||||
let mut field = line.split(":").collect::<Vec<&str>>();
|
||||
let mut users = field.last().unwrap().split(",").collect::<Vec<&str>>();
|
||||
users = users
|
||||
.into_iter()
|
||||
.filter(|username| !username.is_empty())
|
||||
.collect::<Vec<&str>>();
|
||||
if field[0].eq(info.group.as_str()) && !users.contains(&info.username.as_str()) {
|
||||
users.push(info.username.as_str());
|
||||
}
|
||||
|
||||
let new_users = users.join(",");
|
||||
field[3] = new_users.as_str();
|
||||
new_content.push_str(format!("{}\n", field.join(":")).as_str());
|
||||
}
|
||||
guard.gshadow_file.set_len(0).unwrap();
|
||||
guard
|
||||
.gshadow_file
|
||||
.seek(std::io::SeekFrom::Start(0))
|
||||
.unwrap();
|
||||
guard
|
||||
.gshadow_file
|
||||
.write_all(new_content.as_bytes())
|
||||
.unwrap();
|
||||
guard.gshadow_file.flush().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
/// userdel执行器
|
||||
pub struct UDelExecutor;
|
||||
|
||||
impl UDelExecutor {
|
||||
/// **执行userdel**
|
||||
///
|
||||
/// ## 参数
|
||||
/// - `info`: 用户信息
|
||||
pub fn execute(info: UDelInfo) {
|
||||
// 移除home目录
|
||||
if let Some(home) = info.home.clone() {
|
||||
std::fs::remove_dir_all(home).unwrap();
|
||||
}
|
||||
|
||||
Self::update_passwd_file(&info);
|
||||
Self::update_shadow_file(&info);
|
||||
Self::update_group_file(&info);
|
||||
Self::update_gshadow_file(&info);
|
||||
}
|
||||
|
||||
/// 更新/etc/passwd文件: 删除用户信息
|
||||
fn update_passwd_file(info: &UDelInfo) {
|
||||
let mut guard = GLOBAL_FILE.lock().unwrap();
|
||||
let content = read_to_string(&guard.passwd_file);
|
||||
let lines: Vec<&str> = content.lines().collect();
|
||||
let new_content = lines
|
||||
.into_iter()
|
||||
.filter(|&line| {
|
||||
let field = line.split(':').collect::<Vec<&str>>();
|
||||
field[0] != info.username.as_str()
|
||||
})
|
||||
.collect::<Vec<&str>>()
|
||||
.join("\n");
|
||||
|
||||
guard.passwd_file.set_len(0).unwrap();
|
||||
guard.passwd_file.seek(std::io::SeekFrom::Start(0)).unwrap();
|
||||
guard.passwd_file.write_all(new_content.as_bytes()).unwrap();
|
||||
guard.passwd_file.flush().unwrap();
|
||||
}
|
||||
|
||||
/// 更新/etc/group文件: 将用户从组中移除
|
||||
fn update_group_file(info: &UDelInfo) {
|
||||
let mut guard = GLOBAL_FILE.lock().unwrap();
|
||||
let content = read_to_string(&guard.group_file);
|
||||
let mut new_content = String::new();
|
||||
for line in content.lines() {
|
||||
let mut field = line.split(':').collect::<Vec<&str>>();
|
||||
let mut users = field.last().unwrap().split(",").collect::<Vec<&str>>();
|
||||
if users.contains(&info.username.as_str()) {
|
||||
field.remove(field.len() - 1);
|
||||
users.remove(
|
||||
users
|
||||
.iter()
|
||||
.position(|&x| x == info.username.as_str())
|
||||
.unwrap(),
|
||||
);
|
||||
let users = users.join(",");
|
||||
field.push(&users.as_str());
|
||||
new_content.push_str(format!("{}\n", field.join(":").as_str()).as_str());
|
||||
} else {
|
||||
new_content.push_str(format!("{}\n", field.join(":").as_str()).as_str());
|
||||
}
|
||||
|
||||
guard.group_file.set_len(0).unwrap();
|
||||
guard.group_file.seek(std::io::SeekFrom::Start(0)).unwrap();
|
||||
guard.group_file.write_all(new_content.as_bytes()).unwrap();
|
||||
guard.group_file.flush().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
/// 更新/etc/shadow文件: 将用户信息删去
|
||||
fn update_shadow_file(info: &UDelInfo) {
|
||||
let mut guard = GLOBAL_FILE.lock().unwrap();
|
||||
let content = read_to_string(&guard.shadow_file);
|
||||
let lines: Vec<&str> = content.lines().collect();
|
||||
let new_content = lines
|
||||
.into_iter()
|
||||
.filter(|&line| !line.contains(&info.username))
|
||||
.collect::<Vec<&str>>()
|
||||
.join("\n");
|
||||
|
||||
guard.shadow_file.set_len(0).unwrap();
|
||||
guard.shadow_file.seek(std::io::SeekFrom::Start(0)).unwrap();
|
||||
guard.shadow_file.write_all(new_content.as_bytes()).unwrap();
|
||||
guard.shadow_file.flush().unwrap();
|
||||
}
|
||||
|
||||
/// 更新/etc/gshadow文件: 将用户从组中移除
|
||||
fn update_gshadow_file(info: &UDelInfo) {
|
||||
let mut guard = GLOBAL_FILE.lock().unwrap();
|
||||
let content = read_to_string(&guard.gshadow_file);
|
||||
let mut new_content = String::new();
|
||||
for line in content.lines() {
|
||||
let mut field = line.split(':').collect::<Vec<&str>>();
|
||||
let mut users = field.last().unwrap().split(",").collect::<Vec<&str>>();
|
||||
if users.contains(&info.username.as_str()) {
|
||||
field.remove(field.len() - 1);
|
||||
users.remove(
|
||||
users
|
||||
.iter()
|
||||
.position(|&x| x == info.username.as_str())
|
||||
.unwrap(),
|
||||
);
|
||||
let users = users.join(",");
|
||||
field.push(&users.as_str());
|
||||
new_content.push_str(format!("{}\n", field.join(":").as_str()).as_str());
|
||||
} else {
|
||||
new_content.push_str(format!("{}\n", field.join(":").as_str()).as_str());
|
||||
}
|
||||
|
||||
guard.gshadow_file.set_len(0).unwrap();
|
||||
guard
|
||||
.gshadow_file
|
||||
.seek(std::io::SeekFrom::Start(0))
|
||||
.unwrap();
|
||||
guard
|
||||
.gshadow_file
|
||||
.write_all(new_content.as_bytes())
|
||||
.unwrap();
|
||||
guard.gshadow_file.flush().unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// usermod执行器
|
||||
pub struct UModExecutor;
|
||||
|
||||
impl UModExecutor {
|
||||
/// **执行usermod**
|
||||
///
|
||||
/// ## 参数
|
||||
/// - `info`: 用户信息
|
||||
pub fn execute(mut info: UModInfo) {
|
||||
// 创建new_home
|
||||
if let Some(new_home) = &info.new_home {
|
||||
let dir_builder = fs::DirBuilder::new();
|
||||
if dir_builder.create(new_home.clone()).is_err() {
|
||||
ErrorHandler::error_handle(
|
||||
format!("unable to create {}", new_home),
|
||||
ExitStatus::CreateHomeFail,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Self::update_passwd_file(&info);
|
||||
Self::update_shadow_file(&info);
|
||||
Self::update_group_file(&mut info);
|
||||
Self::update_gshadow_file(&info);
|
||||
}
|
||||
|
||||
/// 更新/etc/passwd文件的username、uid、comment、home、shell
|
||||
fn update_passwd_file(info: &UModInfo) {
|
||||
let mut new_content = String::new();
|
||||
let mut guard = GLOBAL_FILE.lock().unwrap();
|
||||
let content = read_to_string(&guard.passwd_file);
|
||||
for line in content.lines() {
|
||||
let mut fields = line.split(':').collect::<Vec<&str>>();
|
||||
if fields[0] == info.username {
|
||||
if let Some(new_username) = &info.new_name {
|
||||
fields[0] = new_username;
|
||||
}
|
||||
if let Some(new_uid) = &info.new_uid {
|
||||
fields[2] = new_uid;
|
||||
}
|
||||
if let Some(new_gid) = &info.new_gid {
|
||||
fields[3] = new_gid;
|
||||
}
|
||||
if let Some(new_comment) = &info.new_comment {
|
||||
fields[4] = new_comment;
|
||||
}
|
||||
if let Some(new_home) = &info.new_home {
|
||||
fields[5] = new_home;
|
||||
}
|
||||
if let Some(new_shell) = &info.new_shell {
|
||||
fields[6] = new_shell;
|
||||
}
|
||||
new_content.push_str(format!("{}\n", fields.join(":")).as_str());
|
||||
} else {
|
||||
new_content.push_str(format!("{}\n", line).as_str());
|
||||
}
|
||||
|
||||
guard.passwd_file.set_len(0).unwrap();
|
||||
guard.passwd_file.seek(std::io::SeekFrom::Start(0)).unwrap();
|
||||
guard.passwd_file.write_all(new_content.as_bytes()).unwrap();
|
||||
guard.passwd_file.flush().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
/// 更新/etc/group文件中各用户组中的用户
|
||||
fn update_group_file(info: &mut UModInfo) {
|
||||
let mut name = info.username.clone();
|
||||
if let Some(new_name) = &info.new_name {
|
||||
name = new_name.clone();
|
||||
}
|
||||
let mut new_content = String::new();
|
||||
let mut guard = GLOBAL_FILE.lock().unwrap();
|
||||
let content = read_to_string(&guard.group_file);
|
||||
for line in content.lines() {
|
||||
let mut fields = line.split(':').collect::<Vec<&str>>();
|
||||
let mut users = fields[3].split(",").collect::<Vec<&str>>();
|
||||
users = users
|
||||
.into_iter()
|
||||
.filter(|username| !username.is_empty())
|
||||
.collect::<Vec<&str>>();
|
||||
if let Some(idx) = users.iter().position(|&r| r == info.username) {
|
||||
if let Some(gid) = &info.new_gid {
|
||||
// 换组,将用户从当前组删去
|
||||
if gid != fields[2] {
|
||||
users.remove(idx);
|
||||
} else {
|
||||
info.new_group = Some(fields[0].to_string())
|
||||
}
|
||||
} else {
|
||||
// 不换组但是要更新名字
|
||||
users[idx] = &name;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(groups) = &info.groups {
|
||||
if groups.contains(&fields[0].to_string()) && !users.contains(&name.as_str()) {
|
||||
users.push(&name);
|
||||
}
|
||||
}
|
||||
|
||||
let new_users = users.join(",");
|
||||
fields[3] = new_users.as_str();
|
||||
new_content.push_str(format!("{}\n", fields.join(":")).as_str());
|
||||
}
|
||||
|
||||
guard.group_file.set_len(0).unwrap();
|
||||
guard.group_file.seek(std::io::SeekFrom::Start(0)).unwrap();
|
||||
guard.group_file.write_all(new_content.as_bytes()).unwrap();
|
||||
guard.group_file.flush().unwrap();
|
||||
}
|
||||
|
||||
/// 更新/etc/shadow文件的username
|
||||
fn update_shadow_file(info: &UModInfo) {
|
||||
if let Some(new_name) = &info.new_name {
|
||||
let mut new_content = String::new();
|
||||
let mut guard = GLOBAL_FILE.lock().unwrap();
|
||||
let content = read_to_string(&guard.shadow_file);
|
||||
for line in content.lines() {
|
||||
let mut fields = line.split(':').collect::<Vec<&str>>();
|
||||
if fields[0] == info.username {
|
||||
fields[0] = new_name;
|
||||
new_content.push_str(format!("{}\n", fields.join(":")).as_str());
|
||||
} else {
|
||||
new_content.push_str(format!("{}\n", line).as_str());
|
||||
}
|
||||
}
|
||||
|
||||
guard.shadow_file.set_len(0).unwrap();
|
||||
guard.shadow_file.seek(std::io::SeekFrom::Start(0)).unwrap();
|
||||
guard.shadow_file.write_all(new_content.as_bytes()).unwrap();
|
||||
guard.shadow_file.flush().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
/// 更新/etc/gshadow文件中各用户组中的用户
|
||||
fn update_gshadow_file(info: &UModInfo) {
|
||||
let mut name = info.username.clone();
|
||||
if let Some(new_name) = &info.new_name {
|
||||
name = new_name.clone();
|
||||
}
|
||||
let mut new_content = String::new();
|
||||
let mut guard = GLOBAL_FILE.lock().unwrap();
|
||||
let content = read_to_string(&guard.gshadow_file);
|
||||
for line in content.lines() {
|
||||
let mut fields = line.split(':').collect::<Vec<&str>>();
|
||||
let mut users = fields[3].split(",").collect::<Vec<&str>>();
|
||||
users = users
|
||||
.into_iter()
|
||||
.filter(|username| !username.is_empty())
|
||||
.collect::<Vec<&str>>();
|
||||
if let Some(idx) = users.iter().position(|&r| r == info.username) {
|
||||
if let Some(group) = &info.new_group {
|
||||
// 换组,将用户从当前组删去
|
||||
if group != fields[0] {
|
||||
users.remove(idx);
|
||||
}
|
||||
} else {
|
||||
// 不换组但是要更新名字
|
||||
users[idx] = &name;
|
||||
}
|
||||
}
|
||||
|
||||
let tmp = format!(",{}", name);
|
||||
if let Some(groups) = &info.groups {
|
||||
if groups.contains(&fields[0].to_string()) && !users.contains(&name.as_str()) {
|
||||
if users.is_empty() {
|
||||
users.push(&name);
|
||||
} else {
|
||||
users.push(tmp.as_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let new_users = users.join(",");
|
||||
fields[3] = new_users.as_str();
|
||||
new_content.push_str(format!("{}\n", fields.join(":")).as_str());
|
||||
}
|
||||
|
||||
guard.gshadow_file.set_len(0).unwrap();
|
||||
guard
|
||||
.gshadow_file
|
||||
.seek(std::io::SeekFrom::Start(0))
|
||||
.unwrap();
|
||||
guard
|
||||
.gshadow_file
|
||||
.write_all(new_content.as_bytes())
|
||||
.unwrap();
|
||||
guard.gshadow_file.flush().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
/// passwd执行器
|
||||
pub struct PasswdExecutor;
|
||||
|
||||
impl PasswdExecutor {
|
||||
/// **执行passwd**
|
||||
///
|
||||
/// ## 参数
|
||||
/// - `info`: 用户密码信息
|
||||
pub fn execute(info: PasswdInfo) {
|
||||
Self::update_passwd_file(&info);
|
||||
Self::update_shadow_file(&info);
|
||||
}
|
||||
|
||||
/// 更新/etc/passwd文件: 修改用户密码
|
||||
fn update_passwd_file(info: &PasswdInfo) {
|
||||
let mut new_content = String::new();
|
||||
let mut guard = GLOBAL_FILE.lock().unwrap();
|
||||
let content = read_to_string(&guard.passwd_file);
|
||||
for line in content.lines() {
|
||||
let mut field = line.split(':').collect::<Vec<_>>();
|
||||
if field[0] == info.username {
|
||||
if info.new_password.is_empty() {
|
||||
field[1] = "";
|
||||
} else {
|
||||
field[1] = "x";
|
||||
}
|
||||
}
|
||||
new_content.push_str(format!("{}\n", field.join(":")).as_str());
|
||||
}
|
||||
|
||||
guard.passwd_file.set_len(0).unwrap();
|
||||
guard.passwd_file.seek(std::io::SeekFrom::Start(0)).unwrap();
|
||||
guard.passwd_file.write_all(new_content.as_bytes()).unwrap();
|
||||
guard.passwd_file.flush().unwrap();
|
||||
}
|
||||
|
||||
/// 更新/etc/shadow文件: 修改用户密码
|
||||
fn update_shadow_file(info: &PasswdInfo) {
|
||||
let mut new_content = String::new();
|
||||
let mut guard = GLOBAL_FILE.lock().unwrap();
|
||||
let content = read_to_string(&guard.shadow_file);
|
||||
for line in content.lines() {
|
||||
let mut field = line.split(':').collect::<Vec<_>>();
|
||||
if field[0] == info.username {
|
||||
field[1] = info.new_password.as_str();
|
||||
}
|
||||
new_content.push_str(format!("{}\n", field.join(":")).as_str());
|
||||
}
|
||||
|
||||
guard.shadow_file.set_len(0).unwrap();
|
||||
guard.shadow_file.seek(std::io::SeekFrom::Start(0)).unwrap();
|
||||
guard.shadow_file.write_all(new_content.as_bytes()).unwrap();
|
||||
guard.shadow_file.flush().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
/// groupadd执行器
|
||||
pub struct GAddExecutor;
|
||||
|
||||
impl GAddExecutor {
|
||||
/// **执行groupadd**
|
||||
///
|
||||
/// ## 参数
|
||||
/// - `info`: 组信息
|
||||
pub fn execute(info: GAddInfo) {
|
||||
Self::write_group_file(&info);
|
||||
Self::write_gshadow_file(&info);
|
||||
}
|
||||
|
||||
/// 写入/etc/group文件: 添加用户组信息
|
||||
fn write_group_file(info: &GAddInfo) {
|
||||
GLOBAL_FILE
|
||||
.lock()
|
||||
.unwrap()
|
||||
.group_file
|
||||
.write_all(info.to_string_group().as_bytes())
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
/// 写入/etc/gshadow文件: 添加用户组密码信息
|
||||
fn write_gshadow_file(info: &GAddInfo) {
|
||||
GLOBAL_FILE
|
||||
.lock()
|
||||
.unwrap()
|
||||
.gshadow_file
|
||||
.write_all(info.to_string_gshadow().as_bytes())
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
/// groupdel执行器
|
||||
pub struct GDelExecutor;
|
||||
|
||||
impl GDelExecutor {
|
||||
/// **执行groupdel**
|
||||
///
|
||||
/// ## 参数
|
||||
/// - `info`: 组信息
|
||||
pub fn execute(info: GDelInfo) {
|
||||
Self::update_group_file(&info);
|
||||
Self::update_gshadow_file(&info);
|
||||
}
|
||||
|
||||
/// 更新/etc/group文件:删除用户组
|
||||
pub fn update_group_file(info: &GDelInfo) {
|
||||
let mut new_content = String::new();
|
||||
let mut guard = GLOBAL_FILE.lock().unwrap();
|
||||
let content = read_to_string(&guard.group_file);
|
||||
for line in content.lines() {
|
||||
let field = line.split(':').collect::<Vec<&str>>();
|
||||
if field[0] != info.groupname {
|
||||
new_content.push_str(format!("{}\n", line).as_str());
|
||||
}
|
||||
}
|
||||
|
||||
guard.group_file.set_len(0).unwrap();
|
||||
guard.group_file.seek(std::io::SeekFrom::Start(0)).unwrap();
|
||||
guard.group_file.write_all(new_content.as_bytes()).unwrap();
|
||||
guard.group_file.flush().unwrap();
|
||||
}
|
||||
|
||||
/// 更新/etc/gshadow文件:移除用户组
|
||||
pub fn update_gshadow_file(info: &GDelInfo) {
|
||||
let mut new_content = String::new();
|
||||
let mut guard = GLOBAL_FILE.lock().unwrap();
|
||||
let content = read_to_string(&guard.gshadow_file);
|
||||
for line in content.lines() {
|
||||
let field = line.split(':').collect::<Vec<&str>>();
|
||||
if field[0] != info.groupname {
|
||||
new_content.push_str(format!("{}\n", line).as_str());
|
||||
}
|
||||
}
|
||||
|
||||
guard.gshadow_file.set_len(0).unwrap();
|
||||
guard
|
||||
.gshadow_file
|
||||
.seek(std::io::SeekFrom::Start(0))
|
||||
.unwrap();
|
||||
guard
|
||||
.gshadow_file
|
||||
.write_all(new_content.as_bytes())
|
||||
.unwrap();
|
||||
guard.gshadow_file.flush().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
/// groupmod执行器
|
||||
pub struct GModExecutor;
|
||||
|
||||
impl GModExecutor {
|
||||
/// **执行groupmod**
|
||||
///
|
||||
/// ## 参数
|
||||
/// - `info`: 组信息
|
||||
pub fn execute(info: GModInfo) {
|
||||
Self::update_passwd_file(&info);
|
||||
Self::update_group_file(&info);
|
||||
Self::update_gshadow_file(&info);
|
||||
}
|
||||
|
||||
/// 更新/etc/group文件: 更新用户组信息
|
||||
fn update_group_file(info: &GModInfo) {
|
||||
let mut new_content = String::new();
|
||||
let mut guard = GLOBAL_FILE.lock().unwrap();
|
||||
let content = read_to_string(&guard.group_file);
|
||||
for line in content.lines() {
|
||||
let mut field = line.split(':').collect::<Vec<&str>>();
|
||||
if field[0] == info.groupname {
|
||||
if let Some(new_groupname) = &info.new_groupname {
|
||||
field[0] = new_groupname;
|
||||
}
|
||||
if let Some(new_gid) = &info.new_gid {
|
||||
field[2] = new_gid;
|
||||
}
|
||||
}
|
||||
new_content.push_str(format!("{}\n", field.join(":")).as_str());
|
||||
}
|
||||
|
||||
guard.group_file.set_len(0).unwrap();
|
||||
guard.group_file.seek(std::io::SeekFrom::Start(0)).unwrap();
|
||||
guard.group_file.write_all(new_content.as_bytes()).unwrap();
|
||||
guard.group_file.flush().unwrap();
|
||||
}
|
||||
|
||||
/// 更新/etc/gshadow文件: 更新用户组密码信息
|
||||
fn update_gshadow_file(info: &GModInfo) {
|
||||
let mut new_content = String::new();
|
||||
let mut guard = GLOBAL_FILE.lock().unwrap();
|
||||
let content = read_to_string(&guard.gshadow_file);
|
||||
for line in content.lines() {
|
||||
let mut field = line.split(':').collect::<Vec<&str>>();
|
||||
if field[0] == info.groupname {
|
||||
if let Some(new_groupname) = &info.new_groupname {
|
||||
field[0] = new_groupname;
|
||||
}
|
||||
}
|
||||
new_content.push_str(format!("{}\n", field.join(":")).as_str());
|
||||
}
|
||||
|
||||
guard.gshadow_file.set_len(0).unwrap();
|
||||
guard
|
||||
.gshadow_file
|
||||
.seek(std::io::SeekFrom::Start(0))
|
||||
.unwrap();
|
||||
guard
|
||||
.gshadow_file
|
||||
.write_all(new_content.as_bytes())
|
||||
.unwrap();
|
||||
guard.gshadow_file.flush().unwrap();
|
||||
}
|
||||
|
||||
/// 更新/etc/passwd文件: 更新用户组ID信息,因为用户组ID可能会被修改
|
||||
fn update_passwd_file(info: &GModInfo) {
|
||||
let mut new_content = String::new();
|
||||
let mut guard = GLOBAL_FILE.lock().unwrap();
|
||||
let content = read_to_string(&guard.passwd_file);
|
||||
for line in content.lines() {
|
||||
let mut field = line.split(':').collect::<Vec<&str>>();
|
||||
if field[3] == info.gid {
|
||||
if let Some(new_gid) = &info.new_gid {
|
||||
field[3] = new_gid;
|
||||
}
|
||||
}
|
||||
new_content.push_str(format!("{}\n", field.join(":")).as_str());
|
||||
}
|
||||
|
||||
guard.passwd_file.set_len(0).unwrap();
|
||||
guard.passwd_file.seek(std::io::SeekFrom::Start(0)).unwrap();
|
||||
guard.passwd_file.write_all(new_content.as_bytes()).unwrap();
|
||||
guard.passwd_file.flush().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
fn read_to_string(mut file: &File) -> String {
|
||||
file.seek(std::io::SeekFrom::Start(0)).unwrap();
|
||||
let mut content = String::new();
|
||||
file.read_to_string(&mut content).unwrap();
|
||||
content
|
||||
}
|
2
user/apps/user-manage/src/executor/mod.rs
Normal file
2
user/apps/user-manage/src/executor/mod.rs
Normal file
@ -0,0 +1,2 @@
|
||||
#![allow(dead_code)]
|
||||
pub mod executor;
|
5
user/apps/user-manage/src/lib.rs
Normal file
5
user/apps/user-manage/src/lib.rs
Normal file
@ -0,0 +1,5 @@
|
||||
pub mod check;
|
||||
pub mod cmd;
|
||||
pub mod error;
|
||||
pub mod executor;
|
||||
pub mod parser;
|
96
user/apps/user-manage/src/parser/cmd.rs
Normal file
96
user/apps/user-manage/src/parser/cmd.rs
Normal file
@ -0,0 +1,96 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// 命令类型
|
||||
pub enum CmdType {
|
||||
User,
|
||||
Passwd,
|
||||
Group,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
|
||||
pub enum CmdOption {
|
||||
/// 用户描述
|
||||
Comment,
|
||||
/// 用户主目录
|
||||
Dir,
|
||||
/// 组名
|
||||
Group,
|
||||
/// 组id
|
||||
Gid,
|
||||
/// 终端程序
|
||||
Shell,
|
||||
/// 用户id
|
||||
Uid,
|
||||
/// 删除用户的home目录
|
||||
Remove,
|
||||
/// 添加到其它用户组中
|
||||
Append,
|
||||
/// 修改用户名
|
||||
Login,
|
||||
/// 设置组密码
|
||||
Passwd,
|
||||
/// 修改组名
|
||||
NewGroupName,
|
||||
/// 无效选项
|
||||
Invalid,
|
||||
}
|
||||
|
||||
impl From<String> for CmdOption {
|
||||
fn from(s: String) -> Self {
|
||||
match s.as_str() {
|
||||
"-c" => CmdOption::Comment,
|
||||
"-d" => CmdOption::Dir,
|
||||
"-G" => CmdOption::Group,
|
||||
"-g" => CmdOption::Gid,
|
||||
"-s" => CmdOption::Shell,
|
||||
"-u" => CmdOption::Uid,
|
||||
"-r" => CmdOption::Remove,
|
||||
"-a" => CmdOption::Append,
|
||||
"-l" => CmdOption::Login,
|
||||
"-p" => CmdOption::Passwd,
|
||||
"-n" => CmdOption::NewGroupName,
|
||||
_ => CmdOption::Invalid,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CmdOption> for &str {
|
||||
fn from(option: CmdOption) -> Self {
|
||||
match option {
|
||||
CmdOption::Comment => "-c",
|
||||
CmdOption::Dir => "-d",
|
||||
CmdOption::Group => "-G",
|
||||
CmdOption::Shell => "-s",
|
||||
CmdOption::Uid => "-u",
|
||||
CmdOption::Login => "-l",
|
||||
CmdOption::Append => "-a",
|
||||
CmdOption::Gid => "-g",
|
||||
CmdOption::NewGroupName => "-n",
|
||||
CmdOption::Passwd => "-p",
|
||||
CmdOption::Remove => "-r",
|
||||
CmdOption::Invalid => "Invalid option",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// useradd/userdel/usermod命令
|
||||
#[derive(Debug)]
|
||||
pub struct UserCommand {
|
||||
/// 用户名
|
||||
pub username: String,
|
||||
/// 选项
|
||||
pub options: HashMap<CmdOption, String>,
|
||||
}
|
||||
|
||||
/// passwd命令
|
||||
#[derive(Debug)]
|
||||
pub struct PasswdCommand {
|
||||
pub username: Option<String>,
|
||||
}
|
||||
|
||||
/// groupadd/groupdel/groupmod命令
|
||||
#[derive(Debug)]
|
||||
pub struct GroupCommand {
|
||||
pub groupname: String,
|
||||
pub options: HashMap<CmdOption, String>,
|
||||
}
|
3
user/apps/user-manage/src/parser/mod.rs
Normal file
3
user/apps/user-manage/src/parser/mod.rs
Normal file
@ -0,0 +1,3 @@
|
||||
#![allow(dead_code)]
|
||||
pub mod cmd;
|
||||
pub mod parser;
|
137
user/apps/user-manage/src/parser/parser.rs
Normal file
137
user/apps/user-manage/src/parser/parser.rs
Normal file
@ -0,0 +1,137 @@
|
||||
use super::cmd::{CmdOption, GroupCommand, PasswdCommand, UserCommand};
|
||||
use crate::error::error::{ErrorHandler, ExitStatus};
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// 用户命令(useradd/userdel/usermod)解析器
|
||||
pub struct UserParser;
|
||||
|
||||
impl UserParser {
|
||||
/// **解析用户命令**
|
||||
///
|
||||
/// ## 参数
|
||||
/// - `args`: 用户命令参数
|
||||
///
|
||||
/// ## 返回
|
||||
/// - `UserCommand`: 用户命令
|
||||
pub fn parse(args: Vec<String>) -> UserCommand {
|
||||
let username = args.last().unwrap().clone();
|
||||
let args = &args[1..args.len() - 1];
|
||||
let mut options = HashMap::new();
|
||||
|
||||
let mut idx = 0;
|
||||
loop {
|
||||
if idx >= args.len() {
|
||||
break;
|
||||
}
|
||||
let option: CmdOption = args[idx].clone().into();
|
||||
match option {
|
||||
CmdOption::Invalid => invalid_handle(),
|
||||
CmdOption::Remove => {
|
||||
if idx + 1 < args.len() {
|
||||
let op: &str = option.clone().into();
|
||||
ErrorHandler::error_handle(
|
||||
format!("Invalid arg {} of option: {}", args[idx + 1], op),
|
||||
ExitStatus::InvalidCmdSyntax,
|
||||
)
|
||||
}
|
||||
options.insert(option, "".to_string());
|
||||
}
|
||||
CmdOption::Append => {
|
||||
if idx + 1 >= args.len() || idx + 2 >= args.len() || args[idx + 1] != "-G" {
|
||||
ErrorHandler::error_handle(
|
||||
"Invalid option: -a -G <group1,group2,...>".to_string(),
|
||||
ExitStatus::InvalidCmdSyntax,
|
||||
);
|
||||
}
|
||||
idx += 2;
|
||||
let groups = &args[idx];
|
||||
options.insert(option, groups.clone());
|
||||
}
|
||||
_ => {
|
||||
if idx + 1 >= args.len() {
|
||||
let op: &str = option.clone().into();
|
||||
ErrorHandler::error_handle(
|
||||
format!("Invalid arg of option: {}", op),
|
||||
ExitStatus::InvalidCmdSyntax,
|
||||
);
|
||||
}
|
||||
idx += 1;
|
||||
let value = args[idx].clone();
|
||||
options.insert(option, value);
|
||||
}
|
||||
}
|
||||
idx += 1;
|
||||
}
|
||||
|
||||
UserCommand { username, options }
|
||||
}
|
||||
}
|
||||
|
||||
/// passwd命令解析器
|
||||
pub struct PasswdParser;
|
||||
|
||||
impl PasswdParser {
|
||||
/// **解析passwd命令**
|
||||
///
|
||||
/// ## 参数
|
||||
/// - `args`: passwd命令参数
|
||||
///
|
||||
/// ## 返回
|
||||
/// - `PasswdCommand`: passwd命令
|
||||
pub fn parse(args: Vec<String>) -> PasswdCommand {
|
||||
let mut username = None;
|
||||
if args.len() > 1 {
|
||||
username = Some(args.last().unwrap().clone());
|
||||
}
|
||||
PasswdCommand { username }
|
||||
}
|
||||
}
|
||||
|
||||
/// 组命令(groupadd/groupdel/groupmod)解析器
|
||||
pub struct GroupParser;
|
||||
|
||||
impl GroupParser {
|
||||
/// **解析组命令**
|
||||
///
|
||||
/// ## 参数
|
||||
/// - `args`: 组命令参数
|
||||
///
|
||||
/// ## 返回
|
||||
/// - `GroupCommand`: 组命令
|
||||
pub fn parse(args: Vec<String>) -> GroupCommand {
|
||||
let groupname = args.last().unwrap().clone();
|
||||
let args = &args[1..args.len() - 1];
|
||||
let mut options = HashMap::new();
|
||||
|
||||
let mut idx = 0;
|
||||
loop {
|
||||
if idx >= args.len() {
|
||||
break;
|
||||
}
|
||||
let option: CmdOption = args[idx].clone().into();
|
||||
match option {
|
||||
CmdOption::Invalid => invalid_handle(),
|
||||
_ => {
|
||||
if idx + 1 >= args.len() {
|
||||
let op: &str = option.clone().into();
|
||||
ErrorHandler::error_handle(
|
||||
format!("Invalid arg of option: {}", op),
|
||||
ExitStatus::InvalidCmdSyntax,
|
||||
);
|
||||
}
|
||||
idx += 1;
|
||||
let value = args[idx].clone();
|
||||
options.insert(option, value);
|
||||
}
|
||||
}
|
||||
idx += 1;
|
||||
}
|
||||
|
||||
GroupCommand { groupname, options }
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn invalid_handle() {
|
||||
ErrorHandler::error_handle("Invalid option".to_string(), ExitStatus::InvalidCmdSyntax);
|
||||
}
|
24
user/dadk/config/user_manage-0.1.0.dadk
Normal file
24
user/dadk/config/user_manage-0.1.0.dadk
Normal file
@ -0,0 +1,24 @@
|
||||
{
|
||||
"name": "user_manage_tool",
|
||||
"version": "0.1.0",
|
||||
"description": "用户管理工具",
|
||||
"task_type": {
|
||||
"BuildFromSource": {
|
||||
"Local": {
|
||||
"path": "apps/user-manage"
|
||||
}
|
||||
}
|
||||
},
|
||||
"depends": [],
|
||||
"build": {
|
||||
"build_command": "make install"
|
||||
},
|
||||
"install": {
|
||||
"in_dragonos_path": "/"
|
||||
},
|
||||
"clean": {
|
||||
"clean_command": "make clean"
|
||||
},
|
||||
"envs": [],
|
||||
"target_arch": ["x86_64"]
|
||||
}
|
0
user/sysconfig/etc/group
Normal file
0
user/sysconfig/etc/group
Normal file
0
user/sysconfig/etc/gshadow
Normal file
0
user/sysconfig/etc/gshadow
Normal file
0
user/sysconfig/etc/passwd
Normal file
0
user/sysconfig/etc/passwd
Normal file
0
user/sysconfig/etc/shadow
Normal file
0
user/sysconfig/etc/shadow
Normal file
8
user/sysconfig/home/reach/system/shell.service
Normal file
8
user/sysconfig/home/reach/system/shell.service
Normal file
@ -0,0 +1,8 @@
|
||||
[Unit]
|
||||
Description=Shell
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=/bin/NovaShell
|
||||
Restart=always
|
||||
ExecStartPre=-/bin/about.elf
|
Loading…
x
Reference in New Issue
Block a user