33// For the full copyright and license information, please view the LICENSE
44// file that was distributed with this source code.
55
6- //! Set of functions to manage files and symlinks
6+ //! Set of functions to manage regular files, special files, and links.
77
88// spell-checker:ignore backport
99
1010#[ cfg( unix) ]
1111use libc:: {
1212 S_IFBLK , S_IFCHR , S_IFDIR , S_IFIFO , S_IFLNK , S_IFMT , S_IFREG , S_IFSOCK , S_IRGRP , S_IROTH ,
1313 S_IRUSR , S_ISGID , S_ISUID , S_ISVTX , S_IWGRP , S_IWOTH , S_IWUSR , S_IXGRP , S_IXOTH , S_IXUSR ,
14- mode_t,
14+ mkfifo , mode_t,
1515} ;
1616use std:: collections:: HashSet ;
1717use std:: collections:: VecDeque ;
1818use std:: env;
19+ #[ cfg( unix) ]
20+ use std:: ffi:: CString ;
1921use std:: ffi:: { OsStr , OsString } ;
2022use std:: fs;
2123use std:: fs:: read_dir;
@@ -798,6 +800,37 @@ pub fn get_filename(file: &Path) -> Option<&str> {
798800 file. file_name ( ) . and_then ( |filename| filename. to_str ( ) )
799801}
800802
803+ /// Make a FIFO, also known as a named pipe.
804+ ///
805+ /// This is a safe wrapper for the unsafe [`libc::mkfifo`] function,
806+ /// which makes a [named
807+ /// pipe](https://en.wikipedia.org/wiki/Named_pipe) on Unix systems.
808+ ///
809+ /// # Errors
810+ ///
811+ /// If the named pipe cannot be created.
812+ ///
813+ /// # Examples
814+ ///
815+ /// ```ignore
816+ /// use uucore::fs::make_fifo;
817+ ///
818+ /// make_fifo("my-pipe").expect("failed to create the named pipe");
819+ ///
820+ /// std::thread::spawn(|| { std::fs::write("my-pipe", b"hello").unwrap(); });
821+ /// assert_eq!(std::fs::read("my-pipe").unwrap(), b"hello");
822+ /// ```
823+ #[ cfg( unix) ]
824+ pub fn make_fifo ( path : & Path ) -> std:: io:: Result < ( ) > {
825+ let name = CString :: new ( path. to_str ( ) . unwrap ( ) ) . unwrap ( ) ;
826+ let err = unsafe { mkfifo ( name. as_ptr ( ) , 0o666 ) } ;
827+ if err == -1 {
828+ Err ( std:: io:: Error :: from_raw_os_error ( err) )
829+ } else {
830+ Ok ( ( ) )
831+ }
832+ }
833+
801834#[ cfg( test) ]
802835mod tests {
803836 // Note this useful idiom: importing names from outer (for mod tests) scope.
@@ -807,6 +840,8 @@ mod tests {
807840 #[ cfg( unix) ]
808841 use std:: os:: unix;
809842 #[ cfg( unix) ]
843+ use std:: os:: unix:: fs:: FileTypeExt ;
844+ #[ cfg( unix) ]
810845 use tempfile:: { NamedTempFile , tempdir} ;
811846
812847 struct NormalizePathTestCase < ' a > {
@@ -1039,4 +1074,25 @@ mod tests {
10391074 let file_path = PathBuf :: from ( "~/foo.txt" ) ;
10401075 assert ! ( matches!( get_filename( & file_path) , Some ( "foo.txt" ) ) ) ;
10411076 }
1077+
1078+ #[ cfg( unix) ]
1079+ #[ test]
1080+ fn test_make_fifo ( ) {
1081+ // Create the FIFO in a temporary directory.
1082+ let tempdir = tempdir ( ) . unwrap ( ) ;
1083+ let path = tempdir. path ( ) . join ( "f" ) ;
1084+ assert ! ( make_fifo( & path) . is_ok( ) ) ;
1085+
1086+ // Check that it is indeed a FIFO.
1087+ assert ! ( std:: fs:: metadata( & path) . unwrap( ) . file_type( ) . is_fifo( ) ) ;
1088+
1089+ // Check that we can write to it and read from it.
1090+ //
1091+ // Write and read need to happen in different threads,
1092+ // otherwise `write` would block indefinitely while waiting
1093+ // for the `read`.
1094+ let path2 = path. clone ( ) ;
1095+ std:: thread:: spawn ( move || assert ! ( std:: fs:: write( & path2, b"foo" ) . is_ok( ) ) ) ;
1096+ assert_eq ! ( std:: fs:: read( & path) . unwrap( ) , b"foo" ) ;
1097+ }
10421098}
0 commit comments