@@ -2,27 +2,30 @@ use clap::{crate_version, Arg, ArgAction, Command};
22use uucore:: { error:: UResult , format_usage, help_about, help_usage} ;
33
44#[ cfg( unix) ]
5- use uucore:: error:: USimpleError ;
5+ use uucore:: {
6+ entries:: { Group , Locate } ,
7+ error:: USimpleError ,
8+ process,
9+ } ;
610
711const ABOUT : & str = help_about ! ( "wall.md" ) ;
812const USAGE : & str = help_usage ! ( "wall.md" ) ;
913
1014#[ cfg( unix) ]
1115mod unix {
12- use super :: { UResult , USimpleError } ;
16+ use super :: process;
17+
18+ use uucore:: entries:: { uid2usr, Locate , Passwd } ;
19+ use uucore:: utmpx:: Utmpx ;
1320
14- use chrono:: { DateTime , Local } ;
15- use libc:: { c_char, gid_t} ;
1621 use std:: {
17- ffi:: { CStr , CString } ,
18- fmt:: Write as fw,
22+ ffi:: CStr ,
1923 fs:: OpenOptions ,
2024 io:: { BufRead , BufReader , Read , Write } ,
21- str:: FromStr ,
2225 sync:: { mpsc, Arc } ,
23- time:: { Duration , SystemTime } ,
26+ time:: Duration ,
2427 } ;
25- use unicode_width:: UnicodeWidthChar ;
28+ use unicode_width:: { UnicodeWidthChar , UnicodeWidthStr } ;
2629
2730 const TERM_WIDTH : usize = 79 ;
2831 const BLANK : & str = unsafe { str:: from_utf8_unchecked ( & [ b' ' ; TERM_WIDTH ] ) } ;
@@ -35,51 +38,31 @@ mod unix {
3538 // if group is specified, only print to memebers of the group.
3639 pub fn wall < R : Read > (
3740 input : R ,
38- group : Option < gid_t > ,
41+ group : Option < libc :: gid_t > ,
3942 timeout : Option < & u64 > ,
4043 print_banner : bool ,
4144 ) {
4245 let msg = makemsg ( input, print_banner) ;
4346 let mut seen_ttys = Vec :: with_capacity ( 16 ) ;
44- loop {
45- // get next user entry and check it is valid
46- let entry = unsafe {
47- let utmpptr = libc:: getutxent ( ) ;
48- if utmpptr. is_null ( ) {
49- break ;
50- }
51- & * utmpptr
52- } ;
53-
54- if entry. ut_user [ 0 ] == 0 || entry. ut_type != libc:: USER_PROCESS {
47+ for record in Utmpx :: iter_all_records ( ) {
48+ if !record. is_user_process ( ) {
5549 continue ;
5650 }
5751
5852 // make sure device is valid
59- let first = entry . ut_line [ 0 ] . cast_unsigned ( ) ;
60- if first == 0 || first == b ':' {
53+ let tty = record . tty_device ( ) ;
54+ if tty . is_empty ( ) || tty . starts_with ( ':' ) {
6155 continue ;
6256 }
6357
6458 // check group membership
6559 if let Some ( gid) = group {
66- if !is_gr_member ( & entry. ut_user , gid) {
67- continue ;
60+ match Passwd :: locate ( record. user ( ) . as_str ( ) ) {
61+ Ok ( pw) if pw. gid == gid || pw. belongs_to ( ) . contains ( & gid) => { }
62+ _ => continue ,
6863 }
6964 }
7065
71- // get tty
72- let tty = unsafe {
73- let len = entry
74- . ut_line
75- . iter ( )
76- . position ( |& c| c == 0 )
77- . unwrap_or ( entry. ut_line . len ( ) ) ;
78-
79- let bytes = std:: slice:: from_raw_parts ( entry. ut_line . as_ptr ( ) . cast ( ) , len) ;
80- str:: from_utf8_unchecked ( bytes) . to_owned ( )
81- } ;
82-
8366 // output message to device
8467 if !seen_ttys. contains ( & tty) {
8568 if let Err ( e) = ttymsg ( & tty, msg. clone ( ) , timeout) {
@@ -88,7 +71,6 @@ mod unix {
8871 seen_ttys. push ( tty) ;
8972 }
9073 }
91- unsafe { libc:: endutxent ( ) } ;
9274 }
9375
9476 // Create the banner and sanitise input
@@ -105,16 +87,7 @@ mod unix {
10587 }
10688 } ;
10789
108- let user = unsafe {
109- let ruid = libc:: getuid ( ) ;
110- let pw = libc:: getpwuid ( ruid) ;
111- if !pw. is_null ( ) && !( * pw) . pw_name . is_null ( ) {
112- CStr :: from_ptr ( ( * pw) . pw_name ) . to_string_lossy ( ) . into_owned ( )
113- } else {
114- eprintln ! ( "cannot get passwd uid" ) ;
115- "<someone>" . to_string ( )
116- }
117- } ;
90+ let user = uid2usr ( process:: getuid ( ) ) . unwrap_or ( "<someone>" . to_string ( ) ) ;
11891
11992 let tty = unsafe {
12093 let tty_ptr = libc:: ttyname ( libc:: STDOUT_FILENO ) ;
@@ -126,14 +99,14 @@ mod unix {
12699 }
127100 } ;
128101
129- let date = DateTime :: < Local > :: from ( SystemTime :: now ( ) ) . format ( "%a %b %e %T %Y" ) ;
102+ let date = chrono :: Local :: now ( ) . format ( "%a %b %e %T %Y" ) ;
130103 let banner = format ! ( "Broadcast message from {user}@{hostname} ({tty}) ({date}):" ) ;
131104
132105 blank ( & mut buf) ;
133106 buf += & banner;
134107 buf. extend ( std:: iter:: repeat_n (
135108 ' ' ,
136- TERM_WIDTH . saturating_sub ( banner. len ( ) ) ,
109+ TERM_WIDTH . saturating_sub ( banner. width ( ) ) ,
137110 ) ) ;
138111 buf += "\x07 \x07 \r \n " ;
139112 }
@@ -153,6 +126,8 @@ mod unix {
153126 // - wraps lines by TERM_WIDTH
154127 // - escapes control characters
155128 fn sanitise_line ( line : & str ) -> String {
129+ use std:: fmt:: Write ;
130+
156131 let mut buf = String :: with_capacity ( line. len ( ) ) ;
157132 let mut col = 0 ;
158133
@@ -179,7 +154,7 @@ mod unix {
179154 }
180155 _ => {
181156 buf. push ( ch) ;
182- col += ch. width_cjk ( ) . unwrap_or_default ( ) ;
157+ col += ch. width ( ) . unwrap_or_default ( ) ;
183158 }
184159 }
185160
@@ -195,63 +170,6 @@ mod unix {
195170 buf + "\r \n "
196171 }
197172
198- // Determine if user is in specified group
199- fn is_gr_member ( user : & [ c_char ] , gid : gid_t ) -> bool {
200- // make sure user exists in database
201- let pw = unsafe { libc:: getpwnam ( user. as_ptr ( ) ) } ;
202- if pw. is_null ( ) {
203- return false ;
204- }
205-
206- // if so, check if primary group matches
207- let group = unsafe { ( * pw) . pw_gid } ;
208- if gid == group {
209- return true ;
210- }
211-
212- // on macos, getgrouplist takes c_int as its group argument
213- #[ cfg( target_os = "macos" ) ]
214- let group = group. cast_signed ( ) ;
215-
216- // otherwise check gid is in list of supplementary groups user belongs to
217- let mut ngroups = 16 ;
218- let mut groups = vec ! [ 0 ; ngroups as usize ] ;
219- while unsafe {
220- libc:: getgrouplist ( user. as_ptr ( ) , group, groups. as_mut_ptr ( ) , & raw mut ngroups)
221- } == -1
222- {
223- // ret -1 means buffer was too small so we resize
224- // according to the returned ngroups value
225- groups. resize ( ngroups as usize , 0 ) ;
226- }
227-
228- #[ cfg( target_os = "macos" ) ]
229- let gid = gid. cast_signed ( ) ;
230- groups. contains ( & gid)
231- }
232-
233- // Try to get corresponding group gid.
234- pub fn get_group_gid ( group : & String ) -> UResult < gid_t > {
235- // first we try as a group name
236- let Ok ( cname) = CString :: from_str ( group) else {
237- return Err ( USimpleError :: new ( 1 , "invalid group argument" ) ) ;
238- } ;
239-
240- let gr = unsafe { libc:: getgrnam ( cname. as_ptr ( ) ) } ;
241- if !gr. is_null ( ) {
242- return Ok ( unsafe { ( * gr) . gr_gid } ) ;
243- }
244-
245- // otherwise, try as literal gid
246- let Ok ( gid) = group. parse :: < gid_t > ( ) else {
247- return Err ( USimpleError :: new ( 1 , "invalid group argument" ) ) ;
248- } ;
249- if unsafe { libc:: getgrgid ( gid) } . is_null ( ) {
250- return Err ( USimpleError :: new ( 1 , format ! ( "{group}: unknown gid" ) ) ) ;
251- }
252- Ok ( gid)
253- }
254-
255173 // Write to the tty device
256174 fn ttymsg ( tty : & str , msg : Arc < String > , timeout : Option < & u64 > ) -> Result < ( ) , & ' static str > {
257175 let ( tx, rx) = mpsc:: channel ( ) ;
@@ -337,7 +255,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
337255
338256 // get nobanner flag and check if user is root
339257 let flag = args. get_flag ( "nobanner" ) ;
340- let print_banner = if flag && unsafe { libc :: geteuid ( ) } != 0 {
258+ let print_banner = if flag && process :: geteuid ( ) != 0 {
341259 eprintln ! ( "wall: --nobanner is available only for root" ) ;
342260 true
343261 } else {
@@ -347,7 +265,11 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
347265 // if group exists, map to corresponding gid
348266 let group = args
349267 . get_one :: < String > ( "group" )
350- . map ( unix:: get_group_gid)
268+ . map ( |g| {
269+ Group :: locate ( g. as_str ( ) )
270+ . map ( |g| g. gid )
271+ . map_err ( |_| USimpleError :: new ( 1 , format ! ( "{g}: unknown group" ) ) )
272+ } )
351273 . transpose ( ) ?;
352274
353275 // If we have a single input arg and it exists on disk, treat as a file.
@@ -365,14 +287,12 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
365287 // When we are not root, but suid or sgid, refuse to read files
366288 // (e.g. device files) that the user may not have access to.
367289 // After all, our invoker can easily do "wall < file" instead of "wall file".
368- unsafe {
369- let uid = libc:: getuid ( ) ;
370- if uid > 0 && ( uid != libc:: geteuid ( ) || libc:: getgid ( ) != libc:: getegid ( ) ) {
371- return Err ( USimpleError :: new (
372- 1 ,
373- format ! ( "will not read {fname} - use stdin" ) ,
374- ) ) ;
375- }
290+ let uid = process:: getuid ( ) ;
291+ if uid > 0 && ( uid != process:: geteuid ( ) || process:: getgid ( ) != process:: getegid ( ) ) {
292+ return Err ( USimpleError :: new (
293+ 1 ,
294+ format ! ( "will not read {fname} - use stdin" ) ,
295+ ) ) ;
376296 }
377297
378298 let Ok ( f) = File :: open ( p) else {
0 commit comments