github.com/iDigitalFlame/xmt@v0.5.4/com/pipe/pipe_nix.go (about) 1 //go:build !windows 2 // +build !windows 3 4 // Copyright (C) 2020 - 2023 iDigitalFlame 5 // 6 // This program is free software: you can redistribute it and/or modify 7 // it under the terms of the GNU General Public License as published by 8 // the Free Software Foundation, either version 3 of the License, or 9 // any later version. 10 // 11 // This program is distributed in the hope that it will be useful, 12 // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 // GNU General Public License for more details. 15 // 16 // You should have received a copy of the GNU General Public License 17 // along with this program. If not, see <https://www.gnu.org/licenses/>. 18 // 19 20 package pipe 21 22 import ( 23 "context" 24 "net" 25 "os" 26 "strconv" 27 "strings" 28 "time" 29 30 "github.com/iDigitalFlame/xmt/com" 31 "github.com/iDigitalFlame/xmt/util" 32 "github.com/iDigitalFlame/xmt/util/xerr" 33 ) 34 35 var dialer = new(net.Dialer) 36 37 type listener struct { 38 _ [0]func() 39 net.Listener 40 p string 41 } 42 43 func (l *listener) Close() error { 44 if err := l.Listener.Close(); err != nil { 45 return err 46 } 47 return os.Remove(l.p) 48 } 49 50 // Dial connects to the specified Pipe path. This function will return a 'net.Conn' 51 // instance or any errors that may occur during the connection attempt. 52 // 53 // Pipe names are in the form of "/<path>". 54 // 55 // This function blocks indefinitely. Use the DialTimeout or DialContext to specify 56 // a control method. 57 func Dial(path string) (net.Conn, error) { 58 return dialer.Dial(com.NameUnix, path) 59 } 60 61 // Listen returns a 'net.Listener' that will listen for new connections on the 62 // Named Pipe path specified or any errors that may occur during listener 63 // creation. 64 // 65 // Pipe names are in the form of "/<path>". 66 func Listen(path string) (net.Listener, error) { 67 return ListenPermsContext(context.Background(), path, "") 68 } 69 func stringToDec(s string) (os.FileMode, error) { 70 if v, err := strconv.ParseInt(s, 8, 32); err == nil { 71 return os.FileMode(v), nil 72 } 73 var p os.FileMode 74 for i, c := range s { 75 switch { 76 case i < 3 && (c == 'u' || c == 'U'): 77 p |= os.ModeSetuid 78 case i < 3 && (c == 'g' || c == 'G'): 79 p |= os.ModeSetgid 80 case i == 0 && (c == 't' || c == 'T'): 81 p |= os.ModeSticky 82 case i < 3 && (c == 'r' || c == 'R'): 83 p |= 0400 84 case i < 3 && (c == 'w' || c == 'W'): 85 p |= 0200 86 case i < 3 && (c == 'x' || c == 'X'): 87 p |= 0100 88 case i >= 3 && i < 6 && (c == 'r' || c == 'R'): 89 p |= 0040 90 case i >= 3 && i < 6 && (c == 'w' || c == 'W'): 91 p |= 0020 92 case i >= 3 && i < 6 && (c == 'x' || c == 'X'): 93 p |= 0010 94 case i >= 6 && (c == 'r' || c == 'R'): 95 p |= 0004 96 case i >= 6 && (c == 'w' || c == 'W'): 97 p |= 0002 98 case i >= 6 && (c == 'x' || c == 'X'): 99 p |= 0001 100 case c == '-' || c == ' ': 101 case c != 'r' && c != 'R' && c != 'x' && c != 'X' && c != 'w' && c != 'W': 102 if xerr.ExtendedInfo { 103 return 0, xerr.Sub(`invalid permission "`+s+`"`, 0x2E) 104 } 105 return 0, xerr.Sub("invalid permissions", 0x2E) 106 } 107 } 108 return p, nil 109 } 110 func getPerms(s string) (os.FileMode, int, int, error) { 111 if i := strings.IndexByte(s, ';'); i == -1 { 112 p, err := stringToDec(s) 113 return p, -1, -1, err 114 } 115 v := strings.Split(s, ";") 116 if len(v) > 3 { 117 if xerr.ExtendedInfo { 118 return 0, -1, -1, xerr.Sub(`invalid permission "`+s+`" size `+util.Uitoa(uint64(len(v))), 0x2F) 119 } 120 return 0, -1, -1, xerr.Sub("invalid permission size", 0x2F) 121 } 122 var ( 123 u, g = -1, -1 124 p, err = stringToDec(v[0]) 125 ) 126 if err != nil { 127 return 0, -1, -1, err 128 } 129 if len(v) == 3 && len(v[2]) > 0 { 130 if g, err = strconv.Atoi(v[2]); err != nil { 131 return 0, -1, -1, xerr.Wrap("invalid GID", err) 132 } 133 } 134 if len(v) >= 2 && len(v[1]) > 0 { 135 if u, err = strconv.Atoi(v[1]); err != nil { 136 return 0, -1, -1, xerr.Wrap("invalid UID", err) 137 } 138 } 139 return p, u, g, nil 140 } 141 142 // ListenPerms returns a Listener that will listen for new connections on the 143 // Named Pipe path specified or any errors that may occur during listener 144 // creation. 145 // 146 // Pipe names are in the form of "/<path>". 147 // 148 // This function allows for specifying a Linux permissions string used to set the 149 // permissions of the listening Pipe. 150 func ListenPerms(path, perms string) (net.Listener, error) { 151 return ListenPermsContext(context.Background(), path, perms) 152 } 153 154 // DialTimeout connects to the specified Pipe path. This function will return a 155 // net.Conn instance or any errors that may occur during the connection attempt. 156 // 157 // Pipe names are in the form of "/<path>". 158 // 159 // This function blocks for the specified amount of time and will return 'ErrTimeout' 160 // if the timeout is reached. 161 func DialTimeout(path string, t time.Duration) (net.Conn, error) { 162 return net.DialTimeout(com.NameUnix, path, t) 163 } 164 165 // DialContext connects to the specified Pipe path. This function will return a 166 // net.Conn instance or any errors that may occur during the connection attempt. 167 // 168 // Pipe names are in the form of "/<path>". 169 // 170 // This function blocks until the supplied context is canceled and will return the 171 // context's Err() if the cancel occurs before the connection. 172 func DialContext(x context.Context, path string) (net.Conn, error) { 173 return dialer.DialContext(x, com.NameUnix, path) 174 } 175 176 // ListenContext returns a 'net.Listener' that will listen for new connections 177 // on the Named Pipe path specified or any errors that may occur during listener 178 // creation. 179 // 180 // Pipe names are in the form of "/<path>". 181 // 182 // The provided Context can be used to cancel the Listener. 183 func ListenContext(x context.Context, path string) (net.Listener, error) { 184 return ListenPermsContext(x, path, "") 185 } 186 187 // ListenPermsContext returns a Listener that will listen for new connections on 188 // the Named Pipe path specified or any errors that may occur during listener 189 // creation. 190 // 191 // Pipe names are in the form of "/". 192 // 193 // This function allows for specifying a Linux permissions string used to set the 194 // permissions of the listening Pipe. 195 // 196 // The provided Context can be used to cancel the Listener. 197 func ListenPermsContext(x context.Context, path, perms string) (net.Listener, error) { 198 l, err := com.ListenConfig.Listen(x, com.NameUnix, path) 199 if err != nil { 200 return nil, err 201 } 202 if len(perms) == 0 { 203 return &listener{Listener: l, p: path}, err 204 } 205 m, u, g, err := getPerms(perms) 206 if err != nil { 207 l.Close() 208 return nil, err 209 } 210 if m > 0 { 211 if err := os.Chmod(path, m); err != nil { 212 l.Close() 213 return nil, err 214 } 215 } 216 if err := os.Chown(path, u, g); err != nil { 217 l.Close() 218 return nil, err 219 } 220 return &listener{Listener: l, p: path}, nil 221 }