@@ -9,9 +9,9 @@ use clap::crate_version;
99use clap:: { Arg , ArgAction , Command } ;
1010#[ cfg( target_os = "linux" ) ]
1111use libc:: { sysconf, _SC_CLK_TCK} ;
12- use std:: process;
1312#[ cfg( target_os = "linux" ) ]
14- use std:: { collections:: HashMap , fs, path:: Path } ;
13+ use std:: { collections:: HashMap , fs, path:: Path , time:: SystemTime } ;
14+ use std:: { process, time:: Duration } ;
1515#[ cfg( target_os = "linux" ) ]
1616use uucore:: utmpx:: Utmpx ;
1717use uucore:: { error:: UResult , format_usage, help_about, help_usage} ;
@@ -23,7 +23,7 @@ struct UserInfo {
2323 user : String ,
2424 terminal : String ,
2525 login_time : String ,
26- idle_time : String ,
26+ idle_time : Duration , // for better compatibility with old-style outputs
2727 jcpu : String ,
2828 pcpu : String ,
2929 command : String ,
@@ -83,6 +83,51 @@ fn fetch_pcpu_time(pid: i32) -> Result<f64, std::io::Error> {
8383 Ok ( ( utime + stime) / get_clock_tick ( ) as f64 )
8484}
8585
86+ #[ cfg( target_os = "linux" ) ]
87+ fn fetch_idle_time ( tty : String ) -> Result < Duration , std:: io:: Error > {
88+ let path = Path :: new ( "/dev/" ) . join ( tty) ;
89+ let stat = fs:: metadata ( path) ?;
90+ if let Ok ( time) = stat. accessed ( ) {
91+ Ok ( SystemTime :: now ( ) . duration_since ( time) . unwrap_or_default ( ) )
92+ } else {
93+ Ok ( Duration :: ZERO )
94+ }
95+ }
96+
97+ #[ cfg( not( target_os = "linux" ) ) ]
98+ fn _fetch_idle_time ( _tty : String ) -> Result < Duration , std:: io:: Error > {
99+ Ok ( Duration :: ZERO )
100+ }
101+
102+ fn format_time_elapsed ( time : Duration , old_style : bool ) -> Result < String , chrono:: OutOfRangeError > {
103+ let t = chrono:: Duration :: from_std ( time) ?;
104+ if t. num_days ( ) >= 2 {
105+ Ok ( format ! ( "{}days" , t. num_days( ) ) )
106+ } else if t. num_hours ( ) >= 1 {
107+ Ok ( format ! (
108+ "{}:{:02}{}" ,
109+ t. num_hours( ) ,
110+ t. num_minutes( ) % 60 ,
111+ if old_style { "" } else { "m" }
112+ ) )
113+ } else if t. num_minutes ( ) >= 1 {
114+ Ok ( format ! (
115+ "{}:{:02}{}" ,
116+ t. num_minutes( ) % 60 ,
117+ t. num_seconds( ) % 60 ,
118+ if old_style { "m" } else { "" }
119+ ) )
120+ } else if old_style {
121+ Ok ( String :: new ( ) )
122+ } else {
123+ Ok ( format ! (
124+ "{}.{:02}s" ,
125+ t. num_seconds( ) % 60 ,
126+ ( t. num_milliseconds( ) % 1000 ) / 10
127+ ) )
128+ }
129+ }
130+
86131#[ cfg( target_os = "linux" ) ]
87132fn format_time ( time : String ) -> Result < String , chrono:: format:: ParseError > {
88133 let mut t: String = time;
@@ -127,7 +172,7 @@ fn fetch_user_info() -> Result<Vec<UserInfo>, std::io::Error> {
127172 user : entry. user ( ) ,
128173 terminal : entry. tty_device ( ) ,
129174 login_time : format_time ( entry. login_time ( ) . to_string ( ) ) . unwrap_or_default ( ) ,
130- idle_time : "TODO" . into ( ) , // Placeholder, needs actual implementation
175+ idle_time : fetch_idle_time ( entry . tty_device ( ) ) ? ,
131176 jcpu : format ! ( "{:.2}" , jcpu) ,
132177 pcpu : fetch_pcpu_time ( entry. pid ( ) ) . unwrap_or_default ( ) . to_string ( ) ,
133178 command : fetch_cmdline ( entry. pid ( ) ) . unwrap_or_default ( ) ,
@@ -150,6 +195,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
150195
151196 let no_header = matches. get_flag ( "no-header" ) ;
152197 let short = matches. get_flag ( "short" ) ;
198+ let old_style = matches. get_flag ( "old-style" ) ;
153199
154200 match fetch_user_info ( ) {
155201 Ok ( user_info) => {
@@ -167,15 +213,18 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
167213 if short {
168214 println ! (
169215 "{:<9}{:<9}{:<7}{:<}" ,
170- user. user, user. terminal, user. idle_time, user. command
216+ user. user,
217+ user. terminal,
218+ format_time_elapsed( user. idle_time, old_style) . unwrap_or_default( ) ,
219+ user. command
171220 ) ;
172221 } else {
173222 println ! (
174223 "{:<9}{:<9}{:<9}{:<6} {:<7}{:<5}{:<}" ,
175224 user. user,
176225 user. terminal,
177226 user. login_time,
178- user. idle_time,
227+ format_time_elapsed ( user. idle_time, old_style ) . unwrap_or_default ( ) ,
179228 user. jcpu,
180229 user. pcpu,
181230 user. command
@@ -260,9 +309,10 @@ pub fn uu_app() -> Command {
260309#[ cfg( target_os = "linux" ) ]
261310mod tests {
262311 use crate :: {
263- fetch_cmdline, fetch_pcpu_time, fetch_terminal_number, format_time, get_clock_tick,
312+ fetch_cmdline, fetch_pcpu_time, fetch_terminal_number, format_time, format_time_elapsed,
313+ get_clock_tick,
264314 } ;
265- use std:: { fs, path:: Path , process} ;
315+ use std:: { fs, path:: Path , process, time :: Duration } ;
266316
267317 #[ test]
268318 fn test_format_time ( ) {
@@ -283,6 +333,19 @@ mod tests {
283333 )
284334 }
285335
336+ #[ test]
337+ fn test_format_time_elapsed ( ) {
338+ let td = Duration :: new ( 60 * 60 * 18 + 60 * 18 , 0 ) ;
339+ assert_eq ! (
340+ format_time_elapsed( td, false ) . unwrap( ) ,
341+ String :: from( "18:18m" )
342+ ) ;
343+ assert_eq ! (
344+ format_time_elapsed( td, true ) . unwrap( ) ,
345+ String :: from( "18:18" )
346+ ) ;
347+ }
348+
286349 #[ test]
287350 // Get PID of current process and use that for cmdline testing
288351 fn test_fetch_cmdline ( ) {
0 commit comments