11use std:: process:: { Command , ExitStatus } ;
22use std:: env;
3+ use std:: os:: unix:: fs:: PermissionsExt ;
34
45const DNF_PATH : & str = "/usr/lib/HackerOS/dnf" ;
56
67fn print_help ( ) {
78 println ! ( "hacker: A simple package management tool" ) ;
89 println ! ( "Usage: hacker <command> [arguments]" ) ;
910 println ! ( "\n Available commands:" ) ;
10- println ! ( " autoremove Remove unneeded packages" ) ;
11- println ! ( " install <package> Install a package" ) ;
12- println ! ( " remove <package> Remove a package" ) ;
13- println ! ( " list List installed packages" ) ;
14- println ! ( " search <term> Search for packages" ) ;
15- println ! ( " ? Show this help message" ) ;
16- println ! ( "\n Note: Use 'hacker-update' for updates and upgrades." ) ;
11+ println ! ( " autoremove Remove unneeded packages" ) ;
12+ println ! ( " install <packages> Install one or more packages" ) ;
13+ println ! ( " remove <packages> Remove one or more packages" ) ;
14+ println ! ( " list List installed packages" ) ;
15+ println ! ( " search <term> Search for packages" ) ;
16+ println ! ( " clean Clean package cache" ) ;
17+ println ! ( " info <package> Show package information" ) ;
18+ println ! ( " repolist List enabled repositories" ) ;
19+ println ! ( " copr-enable <repo> Enable a COPR repository" ) ;
20+ println ! ( " copr-disable <repo> Disable a COPR repository" ) ;
21+ println ! ( " ? Show this help message" ) ;
22+ println ! ( "\n Note: Use 'hacker-update' for system updates and upgrades." ) ;
1723}
1824
19- fn execute_dnf ( args : Vec < & str > ) -> Result < ExitStatus , String > {
20- let output = Command :: new ( DNF_PATH )
25+ fn execute_dnf ( args : Vec < & str > , use_sudo : bool ) -> Result < ExitStatus , String > {
26+ let mut command = if use_sudo {
27+ let mut cmd = Command :: new ( "sudo" ) ;
28+ cmd. arg ( DNF_PATH ) ;
29+ cmd
30+ } else {
31+ Command :: new ( DNF_PATH )
32+ } ;
33+
34+ let output = command
2135 . args ( & args)
2236 . status ( )
2337 . map_err ( |e| format ! ( "Failed to execute dnf: {}" , e) ) ?;
2438 Ok ( output)
2539}
2640
41+ fn can_run_without_sudo ( ) -> bool {
42+ // Check if user has write permissions to /usr/lib/HackerOS/dnf
43+ if let Ok ( metadata) = std:: fs:: metadata ( DNF_PATH ) {
44+ let permissions = metadata. permissions ( ) ;
45+ let mode = permissions. mode ( ) ;
46+ // Check if executable and writable by user or group
47+ ( mode & 0o111 ) != 0 && ( mode & 0o600 ) != 0
48+ } else {
49+ false
50+ }
51+ }
52+
2753fn main ( ) {
2854 let args: Vec < String > = env:: args ( ) . collect ( ) ;
29-
3055 if args. len ( ) < 2 {
3156 println ! ( "Error: No command provided" ) ;
3257 print_help ( ) ;
3358 std:: process:: exit ( 1 ) ;
3459 }
3560
3661 let command = & args[ 1 ] ;
62+ let use_sudo = !can_run_without_sudo ( ) ;
3763
3864 match command. as_str ( ) {
3965 "autoremove" => {
40- match execute_dnf ( vec ! [ "autoremove" , "-y" ] ) {
66+ match execute_dnf ( vec ! [ "autoremove" , "-y" ] , use_sudo ) {
4167 Ok ( status) if status. success ( ) => println ! ( "Autoremove completed successfully" ) ,
4268 Ok ( _) => println ! ( "Autoremove failed" ) ,
4369 Err ( e) => println ! ( "Error: {}" , e) ,
4470 }
4571 }
4672 "install" => {
4773 if args. len ( ) < 3 {
48- println ! ( "Error: Package name required for install" ) ;
74+ println ! ( "Error: At least one package name required for install" ) ;
4975 std:: process:: exit ( 1 ) ;
5076 }
51- let package = & args[ 2 ] ;
52- match execute_dnf ( vec ! [ "install" , "-y" , package] ) {
53- Ok ( status) if status. success ( ) => println ! ( "Package {} installed successfully" , package) ,
54- Ok ( _) => println ! ( "Failed to install package {}" , package) ,
77+ let packages = & args[ 2 ..] ;
78+ let mut dnf_args = vec ! [ "install" , "-y" ] ;
79+ dnf_args. extend ( packages. iter ( ) . map ( |s| s. as_str ( ) ) ) ;
80+ match execute_dnf ( dnf_args, use_sudo) {
81+ Ok ( status) if status. success ( ) => println ! ( "Package(s) {} installed successfully" , packages. join( " " ) ) ,
82+ Ok ( _) => println ! ( "Failed to install package(s) {}" , packages. join( " " ) ) ,
5583 Err ( e) => println ! ( "Error: {}" , e) ,
5684 }
5785 }
5886 "remove" => {
5987 if args. len ( ) < 3 {
60- println ! ( "Error: Package name required for remove" ) ;
88+ println ! ( "Error: At least one package name required for remove" ) ;
6189 std:: process:: exit ( 1 ) ;
6290 }
63- let package = & args[ 2 ] ;
64- match execute_dnf ( vec ! [ "remove" , "-y" , package] ) {
65- Ok ( status) if status. success ( ) => println ! ( "Package {} removed successfully" , package) ,
66- Ok ( _) => println ! ( "Failed to remove package {}" , package) ,
91+ let packages = & args[ 2 ..] ;
92+ let mut dnf_args = vec ! [ "remove" , "-y" ] ;
93+ dnf_args. extend ( packages. iter ( ) . map ( |s| s. as_str ( ) ) ) ;
94+ match execute_dnf ( dnf_args, use_sudo) {
95+ Ok ( status) if status. success ( ) => println ! ( "Package(s) {} removed successfully" , packages. join( " " ) ) ,
96+ Ok ( _) => println ! ( "Failed to remove package(s) {}" , packages. join( " " ) ) ,
6797 Err ( e) => println ! ( "Error: {}" , e) ,
6898 }
6999 }
70100 "list" => {
71- match execute_dnf ( vec ! [ "list" , "installed" ] ) {
101+ match execute_dnf ( vec ! [ "list" , "installed" ] , use_sudo ) {
72102 Ok ( status) if status. success ( ) => println ! ( "Listed installed packages" ) ,
73103 Ok ( _) => println ! ( "Failed to list packages" ) ,
74104 Err ( e) => println ! ( "Error: {}" , e) ,
@@ -80,12 +110,66 @@ fn main() {
80110 std:: process:: exit ( 1 ) ;
81111 }
82112 let term = & args[ 2 ] ;
83- match execute_dnf ( vec ! [ "search" , term] ) {
113+ match execute_dnf ( vec ! [ "search" , term] , use_sudo ) {
84114 Ok ( status) if status. success ( ) => println ! ( "Search completed" ) ,
85115 Ok ( _) => println ! ( "Search failed" ) ,
86116 Err ( e) => println ! ( "Error: {}" , e) ,
87117 }
88118 }
119+ "clean" => {
120+ match execute_dnf ( vec ! [ "clean" , "all" ] , use_sudo) {
121+ Ok ( status) if status. success ( ) => println ! ( "Package cache cleaned successfully" ) ,
122+ Ok ( _) => println ! ( "Failed to clean package cache" ) ,
123+ Err ( e) => println ! ( "Error: {}" , e) ,
124+ }
125+ }
126+ "info" => {
127+ if args. len ( ) < 3 {
128+ println ! ( "Error: Package name required for info" ) ;
129+ std:: process:: exit ( 1 ) ;
130+ }
131+ let package = & args[ 2 ] ;
132+ match execute_dnf ( vec ! [ "info" , package] , use_sudo) {
133+ Ok ( status) if status. success ( ) => println ! ( "Package information displayed" ) ,
134+ Ok ( _) => println ! ( "Failed to display package information" ) ,
135+ Err ( e) => println ! ( "Error: {}" , e) ,
136+ }
137+ }
138+ "repolist" => {
139+ match execute_dnf ( vec ! [ "repolist" ] , use_sudo) {
140+ Ok ( status) if status. success ( ) => println ! ( "Repository list displayed" ) ,
141+ Ok ( _) => println ! ( "Failed to display repository list" ) ,
142+ Err ( e) => println ! ( "Error: {}" , e) ,
143+ }
144+ }
145+ "copr-enable" => {
146+ if args. len ( ) < 3 {
147+ println ! ( "Error: COPR repository name required" ) ;
148+ std:: process:: exit ( 1 ) ;
149+ }
150+ let repo = & args[ 2 ] ;
151+ match execute_dnf ( vec ! [ "copr" , "enable" , repo] , use_sudo) {
152+ Ok ( status) if status. success ( ) => println ! ( "COPR repository {} enabled" , repo) ,
153+ Ok ( _) => println ! ( "Failed to enable COPR repository {}" , repo) ,
154+ Err ( e) => println ! ( "Error: {}" , e) ,
155+ }
156+ }
157+ "copr-disable" => {
158+ if args. len ( ) < 3 {
159+ println ! ( "Error: COPR repository name required" ) ;
160+ std:: process:: exit ( 1 ) ;
161+ }
162+ let repo = & args[ 2 ] ;
163+ match execute_dnf ( vec ! [ "copr" , "disable" , repo] , use_sudo) {
164+ Ok ( status) if status. success ( ) => println ! ( "COPR repository {} disabled" , repo) ,
165+ Ok ( _) => println ! ( "Failed to disable COPR repository {}" , repo) ,
166+ Err ( e) => println ! ( "Error: {}" , e) ,
167+ }
168+ }
169+ "update" | "upgrade" => {
170+ println ! ( "Error: Use 'hacker-update' for system updates and upgrades." ) ;
171+ std:: process:: exit ( 1 ) ;
172+ }
89173 "?" => {
90174 print_help ( ) ;
91175 }
0 commit comments