Browse Source

feat(user): user management tool (#825)

* 用户管理工具

* 重构

* 改为多个bin文件入口

* bin文件的usage显示自身程序名而非固定程序名
Jomo 9 months ago
parent
commit
03746da3d9

+ 3 - 0
user/apps/user-manage/.gitignore

@@ -0,0 +1,3 @@
+/target
+/Cargo.lock
+/install

+ 36 - 0
user/apps/user-manage/Cargo.toml

@@ -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 - 0
user/apps/user-manage/Makefile

@@ -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 - 0
user/apps/user-manage/README.md

@@ -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 - 0
user/apps/user-manage/src/check/check.rs

@@ -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 - 0
user/apps/user-manage/src/check/info.rs

@@ -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 - 0
user/apps/user-manage/src/check/mod.rs

@@ -0,0 +1,3 @@
+#![allow(dead_code)]
+pub mod check;
+pub mod info;

+ 45 - 0
user/apps/user-manage/src/cmd/groupadd.rs

@@ -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 - 0
user/apps/user-manage/src/cmd/groupdel.rs

@@ -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 - 0
user/apps/user-manage/src/cmd/groupmod.rs

@@ -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 - 0
user/apps/user-manage/src/cmd/mod.rs

@@ -0,0 +1,7 @@
+mod groupadd;
+mod groupdel;
+mod groupmod;
+mod passwd;
+mod useradd;
+mod userdel;
+mod usermod;

+ 25 - 0
user/apps/user-manage/src/cmd/passwd.rs

@@ -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 - 0
user/apps/user-manage/src/cmd/useradd.rs

@@ -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 - 0
user/apps/user-manage/src/cmd/userdel.rs

@@ -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 - 0
user/apps/user-manage/src/cmd/usermod.rs

@@ -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 - 0
user/apps/user-manage/src/error/error.rs

@@ -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 - 0
user/apps/user-manage/src/error/mod.rs

@@ -0,0 +1,2 @@
+#![allow(dead_code)]
+pub mod error;

+ 729 - 0
user/apps/user-manage/src/executor/executor.rs

@@ -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 - 0
user/apps/user-manage/src/executor/mod.rs

@@ -0,0 +1,2 @@
+#![allow(dead_code)]
+pub mod executor;

+ 5 - 0
user/apps/user-manage/src/lib.rs

@@ -0,0 +1,5 @@
+pub mod check;
+pub mod cmd;
+pub mod error;
+pub mod executor;
+pub mod parser;

+ 96 - 0
user/apps/user-manage/src/parser/cmd.rs

@@ -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 - 0
user/apps/user-manage/src/parser/mod.rs

@@ -0,0 +1,3 @@
+#![allow(dead_code)]
+pub mod cmd;
+pub mod parser;

+ 137 - 0
user/apps/user-manage/src/parser/parser.rs

@@ -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 - 0
user/dadk/config/user_manage-0.1.0.dadk

@@ -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 - 0
user/sysconfig/etc/group


+ 0 - 0
user/sysconfig/etc/gshadow


+ 0 - 0
user/sysconfig/etc/passwd


+ 0 - 0
user/sysconfig/etc/shadow


+ 8 - 0
user/sysconfig/home/reach/system/shell.service

@@ -0,0 +1,8 @@
+[Unit]
+Description=Shell
+
+[Service]
+Type=simple
+ExecStart=/bin/NovaShell
+Restart=always
+ExecStartPre=-/bin/about.elf