@@ -51,6 +51,7 @@ pub(crate) fn header(arg: &ArgMatches) -> String {
51
51
)
52
52
}
53
53
54
+ #[ cfg( not( target_os = "linux" ) ) ]
54
55
fn todo ( ) -> String {
55
56
"TODO" . into ( )
56
57
}
@@ -85,9 +86,122 @@ fn uptime() -> String {
85
86
res
86
87
}
87
88
88
- //TODO: Implement active user count
89
+ #[ inline]
90
+ fn format_user ( user : u64 ) -> String {
91
+ match user {
92
+ 0 => "0 user" . to_string ( ) ,
93
+ 1 => "1 user" . to_string ( ) ,
94
+ _ => format ! ( "{} users" , user) ,
95
+ }
96
+ }
97
+
98
+ #[ cfg( target_os = "windows" ) ]
89
99
fn user ( ) -> String {
90
- todo ( )
100
+ use windows:: { core:: * , Win32 :: System :: RemoteDesktop :: * } ;
101
+
102
+ let mut num_user = 0 ;
103
+
104
+ unsafe {
105
+ let mut session_info_ptr = std:: ptr:: null_mut ( ) ;
106
+ let mut session_count = 0 ;
107
+
108
+ WTSEnumerateSessionsW (
109
+ Some ( WTS_CURRENT_SERVER_HANDLE ) ,
110
+ 0 ,
111
+ 1 ,
112
+ & mut session_info_ptr,
113
+ & mut session_count,
114
+ )
115
+ . unwrap ( ) ;
116
+
117
+ let sessions = std:: slice:: from_raw_parts ( session_info_ptr, session_count as usize ) ;
118
+
119
+ for session in sessions {
120
+ let mut buffer = PWSTR :: null ( ) ;
121
+ let mut bytes_returned = 0 ;
122
+
123
+ WTSQuerySessionInformationW (
124
+ Some ( WTS_CURRENT_SERVER_HANDLE ) ,
125
+ session. SessionId ,
126
+ WTS_INFO_CLASS ( 5 ) ,
127
+ & mut buffer,
128
+ & mut bytes_returned,
129
+ )
130
+ . unwrap ( ) ;
131
+
132
+ let username = PWSTR ( buffer. 0 ) . to_string ( ) . unwrap_or_default ( ) ;
133
+ if !username. is_empty ( ) {
134
+ num_user += 1 ;
135
+ }
136
+
137
+ WTSFreeMemory ( buffer. 0 as _ ) ;
138
+ }
139
+
140
+ WTSFreeMemory ( session_info_ptr as _ ) ;
141
+ }
142
+
143
+ format_user ( num_user)
144
+ }
145
+
146
+ #[ cfg( unix) ]
147
+ // see: https://gitlab.com/procps-ng/procps/-/blob/4740a0efa79cade867cfc7b32955fe0f75bf5173/library/uptime.c#L63-L115
148
+ fn user ( ) -> String {
149
+ use libc:: { endutxent, getutxent, setutxent} ;
150
+
151
+ unsafe {
152
+ #[ cfg( target_os = "linux" ) ]
153
+ {
154
+ use libc:: free;
155
+ use libsystemd_sys:: daemon:: sd_booted;
156
+ use libsystemd_sys:: login:: { sd_get_sessions, sd_session_get_class} ;
157
+ use std:: ffi:: { c_char, c_void, CStr } ;
158
+ use std:: ptr;
159
+ // systemd
160
+ if sd_booted ( ) > 0 {
161
+ let mut sessions_list: * mut * mut c_char = ptr:: null_mut ( ) ;
162
+ let mut num_user = 0 ;
163
+ let sessions = sd_get_sessions ( & mut sessions_list) ; // rust-systemd does not implement this
164
+
165
+ if sessions > 0 {
166
+ for i in 0 ..sessions {
167
+ let mut class: * mut c_char = ptr:: null_mut ( ) ;
168
+
169
+ if sd_session_get_class (
170
+ * sessions_list. add ( i as usize ) as * const c_char ,
171
+ & mut class,
172
+ ) < 0
173
+ {
174
+ continue ;
175
+ }
176
+ if CStr :: from_ptr ( class) . to_str ( ) . unwrap ( ) . starts_with ( "user" ) {
177
+ num_user += 1 ;
178
+ }
179
+ free ( class as * mut c_void ) ;
180
+ }
181
+ }
182
+
183
+ for i in 0 ..sessions {
184
+ free ( * sessions_list. add ( i as usize ) as * mut c_void ) ;
185
+ }
186
+ free ( sessions_list as * mut c_void ) ;
187
+
188
+ return format_user ( num_user) ;
189
+ }
190
+ }
191
+
192
+ // utmpx
193
+ let mut num_user = 0 ;
194
+ setutxent ( ) ;
195
+ let mut ut = getutxent ( ) ;
196
+ while !ut. is_null ( ) {
197
+ if ( ( * ut) . ut_type == 7 ) && ( ( * ut) . ut_user [ 0 ] != ( '\0' as i8 ) ) {
198
+ num_user += 1 ;
199
+ }
200
+ ut = getutxent ( ) ;
201
+ }
202
+ endutxent ( ) ;
203
+ format_user ( num_user)
204
+ }
91
205
}
92
206
93
207
#[ cfg( not( target_os = "windows" ) ) ]
@@ -214,9 +328,7 @@ fn memory(arg: &ArgMatches) -> String {
214
328
"e" => ( 1_152_921_504_606_846_976 , "EiB" ) ,
215
329
_ => ( bytesize:: MIB , "MiB" ) ,
216
330
} ,
217
- None => {
218
- ( bytesize:: MIB , "MiB" )
219
- }
331
+ None => ( bytesize:: MIB , "MiB" ) ,
220
332
} ;
221
333
222
334
format ! (
0 commit comments