| OLD | NEW |
| 1 // Copyright 2015 The LUCI Authors. All rights reserved. | 1 // Copyright 2015 The LUCI Authors. All rights reserved. |
| 2 // Use of this source code is governed under the Apache License, Version 2.0 | 2 // Use of this source code is governed under the Apache License, Version 2.0 |
| 3 // that can be found in the LICENSE file. | 3 // that can be found in the LICENSE file. |
| 4 | 4 |
| 5 // +build darwin dragonfly freebsd linux netbsd openbsd | 5 // +build darwin dragonfly freebsd linux netbsd openbsd |
| 6 | 6 |
| 7 package streamserver | 7 package streamserver |
| 8 | 8 |
| 9 import ( | 9 import ( |
| 10 "net" | 10 "net" |
| 11 "os" | 11 "os" |
| 12 | 12 |
| 13 "github.com/luci/luci-go/common/errors" |
| 13 log "github.com/luci/luci-go/common/logging" | 14 log "github.com/luci/luci-go/common/logging" |
| 15 |
| 14 "golang.org/x/net/context" | 16 "golang.org/x/net/context" |
| 15 ) | 17 ) |
| 16 | 18 |
| 17 // NewNamedPipeServer instantiates a new POSIX named pipe server instance. | 19 // maxPOSIXNamedSocketLength is the maximum length of a UNIX domain socket. |
| 18 func NewNamedPipeServer(ctx context.Context, path string) StreamServer { | 20 // |
| 21 // This is defined by the UNIX_PATH_MAX constant, and is usually this value. |
| 22 const maxPOSIXNamedSocketLength = 104 |
| 23 |
| 24 // NewUNIXDomainSocketServer instantiates a new POSIX domain soecket server |
| 25 // instance. |
| 26 // |
| 27 // No resources are actually created until methods are called on the returned |
| 28 // server. |
| 29 func NewUNIXDomainSocketServer(ctx context.Context, path string) (StreamServer,
error) { |
| 30 » switch l := len(path); { |
| 31 » case l == 0: |
| 32 » » return nil, errors.New("cannot have empty path") |
| 33 » case l > maxPOSIXNamedSocketLength: |
| 34 » » return nil, errors.Reason("path exceeds maximum length %(max)d")
. |
| 35 » » » D("path", path). |
| 36 » » » D("max", maxPOSIXNamedSocketLength). |
| 37 » » » Err() |
| 38 » } |
| 39 |
| 19 ctx = log.SetField(ctx, "namedPipePath", path) | 40 ctx = log.SetField(ctx, "namedPipePath", path) |
| 20 return &listenerStreamServer{ | 41 return &listenerStreamServer{ |
| 21 Context: ctx, | 42 Context: ctx, |
| 22 » » gen: func() (net.Listener, error) { | 43 » » gen: func() (net.Listener, string, error) { |
| 23 log.Infof(ctx, "Creating POSIX server socket Listener.") | 44 log.Infof(ctx, "Creating POSIX server socket Listener.") |
| 24 | 45 |
| 25 // Cleanup any previous named pipe. We don't bother chec
king for the file | 46 // Cleanup any previous named pipe. We don't bother chec
king for the file |
| 26 // first since the remove is atomic. We also ignore any
error here, since | 47 // first since the remove is atomic. We also ignore any
error here, since |
| 27 // it's probably related to the file not being found. | 48 // it's probably related to the file not being found. |
| 28 // | 49 // |
| 29 // If there was an actual error removing the file, we'll
catch it shortly | 50 // If there was an actual error removing the file, we'll
catch it shortly |
| 30 // when we try to create it. | 51 // when we try to create it. |
| 31 os.Remove(path) | 52 os.Remove(path) |
| 32 | 53 |
| 33 // Create a UNIX listener | 54 // Create a UNIX listener |
| 34 l, err := net.Listen("unix", path) | 55 l, err := net.Listen("unix", path) |
| 35 if err != nil { | 56 if err != nil { |
| 36 » » » » return nil, err | 57 » » » » return nil, "", err |
| 37 } | 58 } |
| 38 | 59 |
| 60 addr := "unix:" + path |
| 39 ul := selfCleaningUNIXListener{ | 61 ul := selfCleaningUNIXListener{ |
| 40 Context: ctx, | 62 Context: ctx, |
| 41 Listener: l, | 63 Listener: l, |
| 42 path: path, | 64 path: path, |
| 43 } | 65 } |
| 44 » » » return &ul, nil | 66 » » » return &ul, addr, nil |
| 45 }, | 67 }, |
| 46 » } | 68 » }, nil |
| 47 } | 69 } |
| 48 | 70 |
| 49 // Wrapper around the "unix"-type Listener that cleans up the named pipe on | 71 // selfCleaningUNIXListener is a wrapper around the "unix"-type Listener that |
| 50 // creation and 'Close()' | 72 // cleans up the named pipe on creation and Close(). |
| 51 // | 73 // |
| 52 // The standard Go Listener will unlink the file when Closed. However, it | 74 // The standard Go Listener will unlink the file when Closed. However, it |
| 53 // doesn't do it in a deferred, so this will clean up if a panic is encountered | 75 // doesn't do it in a deferred, so this will clean up if a panic is encountered |
| 54 // during close. | 76 // during close. |
| 55 type selfCleaningUNIXListener struct { | 77 type selfCleaningUNIXListener struct { |
| 56 context.Context | 78 context.Context |
| 57 net.Listener | 79 net.Listener |
| 58 | 80 |
| 59 path string | 81 path string |
| 60 } | 82 } |
| 61 | 83 |
| 62 func (l *selfCleaningUNIXListener) Close() error { | 84 func (l *selfCleaningUNIXListener) Close() error { |
| 63 defer func() { | 85 defer func() { |
| 64 if err := os.Remove(l.path); err != nil { | 86 if err := os.Remove(l.path); err != nil { |
| 65 log.Fields{ | 87 log.Fields{ |
| 66 log.ErrorKey: err, | 88 log.ErrorKey: err, |
| 67 }.Debugf(l, "Failed to remove named pipe file on Close()
.") | 89 }.Debugf(l, "Failed to remove named pipe file on Close()
.") |
| 68 } | 90 } |
| 69 }() | 91 }() |
| 70 | 92 |
| 71 if err := l.Listener.Close(); err != nil { | 93 if err := l.Listener.Close(); err != nil { |
| 72 return err | 94 return err |
| 73 } | 95 } |
| 74 return nil | 96 return nil |
| 75 } | 97 } |
| OLD | NEW |