go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/logdog/client/butler/streamserver/namedPipe_posix.go (about) 1 // Copyright 2015 The LUCI Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 //go:build unix 16 // +build unix 17 18 package streamserver 19 20 import ( 21 "context" 22 "io/ioutil" 23 "net" 24 "os" 25 "path/filepath" 26 27 "go.chromium.org/luci/common/errors" 28 "go.chromium.org/luci/common/logging" 29 log "go.chromium.org/luci/common/logging" 30 ) 31 32 // maxPOSIXNamedSocketLength is the maximum length of a UNIX domain socket. 33 // 34 // This is defined by the UNIX_PATH_MAX constant, and is usually this value. 35 const maxPOSIXNamedSocketLength = 104 36 37 // newStreamServer instantiates a new POSIX domain socket server 38 // instance. 39 // 40 // No resources are actually created until methods are called on the returned 41 // server. 42 func newStreamServer(ctx context.Context, path string) (*StreamServer, error) { 43 if path == "" { 44 tFile, err := ioutil.TempFile("", "ld") 45 if err != nil { 46 return nil, err 47 } 48 path = tFile.Name() 49 if err := tFile.Close(); err != nil { 50 return nil, errors.Annotate(err, "closing tempfile %q", path).Err() 51 } 52 } else { 53 abs, err := filepath.Abs(path) 54 if err != nil { 55 return nil, errors.Annotate(err, "could not get absolute path of %q", path).Err() 56 } 57 path = abs 58 } 59 60 if len(path) > maxPOSIXNamedSocketLength { 61 return nil, errors.Reason("path exceeds maximum length %d", maxPOSIXNamedSocketLength).Err() 62 } 63 64 ctx = log.SetField(ctx, "namedPipePath", path) 65 return &StreamServer{ 66 log: logging.Get(ctx), 67 address: "unix:" + path, 68 gen: func() (listener, error) { 69 log.Infof(ctx, "Creating POSIX server socket Listener.") 70 71 // Cleanup any previous named pipe. We don't bother checking for the file 72 // first since the remove is atomic. We also ignore any error here, since 73 // it's probably related to the file not being found. 74 // 75 // If there was an actual error removing the file, we'll catch it shortly 76 // when we try to create it. 77 os.Remove(path) 78 79 // Create a UNIX listener 80 l, err := net.Listen("unix", path) 81 if err != nil { 82 return nil, err 83 } 84 85 ul := selfCleaningUNIXListener{ 86 Context: ctx, 87 Listener: l, 88 path: path, 89 } 90 return mkListener(&ul), nil 91 }, 92 }, nil 93 } 94 95 // selfCleaningUNIXListener is a wrapper around the "unix"-type Listener that 96 // cleans up the named pipe on creation and Close(). 97 // 98 // The standard Go Listener will unlink the file when Closed. However, it 99 // doesn't do it in a deferred, so this will clean up if a panic is encountered 100 // during close. 101 type selfCleaningUNIXListener struct { 102 context.Context 103 net.Listener 104 105 path string 106 } 107 108 func (l *selfCleaningUNIXListener) Close() error { 109 defer func() { 110 if err := os.Remove(l.path); err != nil { 111 log.Fields{ 112 log.ErrorKey: err, 113 }.Debugf(l, "Failed to remove named pipe file on Close().") 114 } 115 }() 116 117 if err := l.Listener.Close(); err != nil { 118 return err 119 } 120 return nil 121 }