Skip to content

skill: add basic implementation of skill #364

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: main
Choose a base branch
from

Conversation

maplestarplayl
Copy link

This pr add the skill command, but only works on Linux

@maplestarplayl
Copy link
Author

Although the main functionality has been completed, some edge cases and potential improvements still need to be considered, such as command usage clarification, test coverage, more elegant implementation and performance optimization.

@Krysztal112233 Krysztal112233 changed the title skill: add basic implementation of sload skill: add basic implementation of skill Mar 24, 2025
Comment on lines 5 to 10
pub const SIGNALS: &[&str] = &[
"HUP", "INT", "QUIT", "ILL", "TRAP", "ABRT", "BUS", "FPE", "KILL", "USR1", "SEGV", "USR2",
"PIPE", "ALRM", "TERM", "STKFLT", "CHLD", "CONT", "STOP", "TSTP", "TTIN", "TTOU", "URG",
"XCPU", "XFSZ", "VTALRM", "PROF", "WINCH", "POLL", "PWR", "SYS",
];
const DEFAULT_SIGNAL: &str = "TERM";
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, got it.
But I found that the defined ALL_SIGNALS have 32 signals in linux , which is not consistent with the Linux Manual (has 31).
Do you know what causes this difference and how should I handle this?
Thanks for help
image

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please use text (code blocks) instead of picture, using text will make it easier to be searched :)

Copy link
Collaborator

@Krysztal112233 Krysztal112233 Mar 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this post can help you to understand the EXIT "signal", which can be considered as a trap to capture something. it's an extension of shell, bash, zsh, fish, etc..

https://unix.stackexchange.com/questions/385030/are-exit-debug-return-and-err-signals

https://fishshell.com/docs/current/cmds/trap.html

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please use text (code blocks) instead of picture, using text will make it easier to be searched :)

Sorry, my mistake. I’ll stick to using text in our future conversations.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this post can help you to understand the EXIT "signal", which can be considered as a trap to capture something. it's an extension of shell, bash, zsh, fish, etc..

https://unix.stackexchange.com/questions/385030/are-exit-debug-return-and-err-signals

https://fishshell.com/docs/current/cmds/trap.html

After reviewing these articles, it appears that signals like EXIT are specific to the shell and not applicable to all processes. Therefore, in the context of the skill command, I have excluded them. Does this approach seem correct?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it can be processed in this way

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, my mistake. I’ll stick to using text in our future

Please don't be nervous, it's not a mistake :)

}
}

pub fn parse_command(args: &mut impl uucore::Args) -> Vec<OsString> {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could you please explain what this function does?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I can explain, i know it looks a little wired :)

Issue Explanation:

The core problem lies in the signal format expected by skill, which follows the structure:
skill [signal] [options] <expression

However, the signal is passed in the form -HUP instead of -s HUP, preventing clap from automatically parsing it from the arguments.

Solution Approach:

To handle this correctly:

  • Signal as an Indexed Argument:
    We must define signal as an indexed argument in clap so it can be properly parsed.
  • Expression as an Indexed Argument
    Since the expression is also passed in a similar way(not an option), it must also be treated as an indexed argument.

But after doing things above, another problem comes in: Clap's Restriction on Optional Arguments:
clap enforces a rule where if a later indexed argument is required, earlier ones cannot be optional.
This means signal must be marked as required to comply with clap's parsing logic, which is conflict with skill's Default Behavior:
The standard skill implementation (procps-ng 3.3.17) allows signal to be optional with a default value.
To reconcile this with clap's requirements, we preprocess the arguments before passing them to clap, ensuring signal is always provided (even if defaulted) to satisfy parsing constraints.

Summary of the Issue

The root cause of these challenges stems from the fact that skill's argument format (as defined in procps-ng 3.3.17) cannot be directly modeled using clap's parsing rules.

However, this doesn’t imply any limitation in clap—rather, skill’s unconventional syntax (e.g., -HUP instead of -s HUP) deviates from typical CLI conventions, making parsing more complex.

Whatever, I will add more comments to the code to make it more reasonable :)
Also, if there are other more elegant way to process that, please tell me, thanks!

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cakebaker can you give us some help? seems like this is something clap can't handle.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@maplestarplayl and if it is possible, could you write some document(comment) and unit tests to make sure it doesn't regressing in future?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@maplestarplayl and if it is possible, could you write some document(comment) and unit tests to make sure it doesn't regressing in future?

Have added corresponding tests in the command.rs file

@Krysztal112233
Copy link
Collaborator

Krysztal112233 commented Mar 24, 2025

Great work, and please don't forget run rustfmt before commit :)

@cakebaker cakebaker linked an issue Mar 26, 2025 that may be closed by this pull request
@maplestarplayl
Copy link
Author

Refactor the util functions with pgrep's process.rs, and add corresponding tests for command and user usage in skill command

@cakebaker
Copy link
Contributor

Can you please run cargo clippy? And the CI shows some issues on macOS :|

Comment on lines +16 to +17
}
#[cfg(target_os = "linux")]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
}
#[cfg(target_os = "linux")]
}
#[cfg(target_os = "linux")]

Comment on lines +36 to +37
}
#[cfg(target_os = "linux")]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
}
#[cfg(target_os = "linux")]
}
#[cfg(target_os = "linux")]

}
#[cfg(target_os = "linux")]
#[test]
fn test_mutiple_signal() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A typo:

Suggested change
fn test_mutiple_signal() {
fn test_multiple_signal() {

Comment on lines +47 to +48
}
#[cfg(target_os = "linux")]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
}
#[cfg(target_os = "linux")]
}
#[cfg(target_os = "linux")]

Comment on lines +58 to +59
}
#[cfg(target_os = "linux")]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
}
#[cfg(target_os = "linux")]
}
#[cfg(target_os = "linux")]

Comment on lines +60 to +77
#[test]
fn test_list_option() {
new_ucmd!()
.arg("-l")
.succeeds()
.no_stderr()
.stdout_contains("HUP INT QUIT ILL TRAP ABRT BUS FPE KILL USR1 SEGV USR2 PIPE ALRM TERM STKFLT CHLD CONT STOP TSTP TTIN TTOU URG XCPU XFSZ VTALRM PROF WINCH POLL PWR SYS");
}

#[cfg(target_os = "linux")]
#[test]
fn test_list_option_long() {
new_ucmd!()
.arg("--list")
.succeeds()
.no_stderr()
.stdout_contains("HUP INT QUIT ILL TRAP ABRT BUS FPE KILL USR1 SEGV USR2 PIPE ALRM TERM STKFLT CHLD CONT STOP TSTP TTIN TTOU URG XCPU XFSZ VTALRM PROF WINCH POLL PWR SYS");
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here it might make sense to combine the functions into one and simply loop through the args, something like:

for arg in ["-l", "--list"] {
    new_ucmd!()
        .arg(arg)
...
}

Comment on lines +79 to +105
#[cfg(target_os = "linux")]
#[test]
fn test_table_option() {
new_ucmd!()
.arg("-L")
.succeeds()
.no_stderr()
.stdout_contains("1 HUP 2 INT 3 QUIT 4 ILL 5 TRAP 6 ABRT 7 BUS")
.stdout_contains("8 FPE 9 KILL 10 USR1 11 SEGV 12 USR2 13 PIPE 14 ALRM")
.stdout_contains("15 TERM 16 STKFLT 17 CHLD 18 CONT 19 STOP 20 TSTP 21 TTIN")
.stdout_contains("22 TTOU 23 URG 24 XCPU 25 XFSZ 26 VTALRM 27 PROF 28 WINCH")
.stdout_contains("29 POLL 30 PWR 31 SYS");
}

#[cfg(target_os = "linux")]
#[test]
fn test_table_option_long() {
new_ucmd!()
.arg("--table")
.succeeds()
.no_stderr()
.stdout_contains("1 HUP 2 INT 3 QUIT 4 ILL 5 TRAP 6 ABRT 7 BUS")
.stdout_contains("8 FPE 9 KILL 10 USR1 11 SEGV 12 USR2 13 PIPE 14 ALRM")
.stdout_contains("15 TERM 16 STKFLT 17 CHLD 18 CONT 19 STOP 20 TSTP 21 TTIN")
.stdout_contains("22 TTOU 23 URG 24 XCPU 25 XFSZ 26 VTALRM 27 PROF 28 WINCH")
.stdout_contains("29 POLL 30 PWR 31 SYS");
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same with these two functions.

Comment on lines +106 to +115

#[cfg(target_os = "linux")]
#[test]
fn test_mutiple_options() {
new_ucmd!()
.arg("-nv") // no action + verbose
.arg("1")
.succeeds()
.stdout_contains("Would send signal TERM to process 1");
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function is identical to test_default_signal, so it can be removed.

Suggested change
#[cfg(target_os = "linux")]
#[test]
fn test_mutiple_options() {
new_ucmd!()
.arg("-nv") // no action + verbose
.arg("1")
.succeeds()
.stdout_contains("Would send signal TERM to process 1");
}

Comment on lines +7 to +8
use uu_pgrep::process::{walk_process, ProcessInformation};
#[cfg(target_os = "linux")]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
use uu_pgrep::process::{walk_process, ProcessInformation};
#[cfg(target_os = "linux")]
use uu_pgrep::process::{walk_process, ProcessInformation};
#[cfg(target_os = "linux")]


#[cfg(target_os = "linux")]
pub fn get_process_owner(process: &mut ProcessInformation) -> Option<String> {
// let status_path = format!("/proc/{}/status", pid);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would either remove this line or add a TODO with a reason why it's commented out.


// Mainly used to parse the signal to make sure it is valid
// and insert the default signal if it's not present
pub fn parse_command(args: &mut impl uucore::Args) -> Vec<String> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As this function can fail, I would return a Result.

Comment on lines +114 to +115
eprintln!("Invalid signal: {}", &signal);
std::process::exit(2);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See my previous comment about returning a Result. We usually use something like:

return Err(USimpleError::new(2, format!("Invalid signal: {}", &signal)));

Comment on lines +123 to +124
eprintln!("Too many signals");
std::process::exit(2);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See my previous comment.

Copy link
Contributor

@cakebaker cakebaker left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall good job :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add command skill
4 participants