blob: 4611c499c15f6c3989e8bf3806f48f189892d392 [file] [log] [blame]
// Copyright 2015 The LUCI Authors.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.
// +build darwin dragonfly freebsd linux netbsd openbsd
package streamserver
import (
log ""
// maxPOSIXNamedSocketLength is the maximum length of a UNIX domain socket.
// This is defined by the UNIX_PATH_MAX constant, and is usually this value.
const maxPOSIXNamedSocketLength = 104
// newStreamServer instantiates a new POSIX domain socket server
// instance.
// No resources are actually created until methods are called on the returned
// server.
func newStreamServer(ctx context.Context, path string) (*StreamServer, error) {
if path == "" {
tFile, err := ioutil.TempFile("", "ld")
if err != nil {
return nil, err
path = tFile.Name()
if err := tFile.Close(); err != nil {
return nil, errors.Annotate(err, "closing tempfile %q", path).Err()
} else {
abs, err := filepath.Abs(path)
if err != nil {
return nil, errors.Annotate(err, "could not get absolute path of %q", path).Err()
path = abs
if len(path) > maxPOSIXNamedSocketLength {
return nil, errors.Reason("path exceeds maximum length %d", maxPOSIXNamedSocketLength).
InternalReason("path(%s)", path).Err()
ctx = log.SetField(ctx, "namedPipePath", path)
return &StreamServer{
log: logging.Get(ctx),
address: "unix:" + path,
gen: func() (listener, error) {
log.Infof(ctx, "Creating POSIX server socket Listener.")
// Cleanup any previous named pipe. We don't bother checking for the file
// first since the remove is atomic. We also ignore any error here, since
// it's probably related to the file not being found.
// If there was an actual error removing the file, we'll catch it shortly
// when we try to create it.
// Create a UNIX listener
l, err := net.Listen("unix", path)
if err != nil {
return nil, err
ul := selfCleaningUNIXListener{
Context: ctx,
Listener: l,
path: path,
return mkListener(&ul), nil
}, nil
// selfCleaningUNIXListener is a wrapper around the "unix"-type Listener that
// cleans up the named pipe on creation and Close().
// The standard Go Listener will unlink the file when Closed. However, it
// doesn't do it in a deferred, so this will clean up if a panic is encountered
// during close.
type selfCleaningUNIXListener struct {
path string
func (l *selfCleaningUNIXListener) Close() error {
defer func() {
if err := os.Remove(l.path); err != nil {
log.ErrorKey: err,
}.Debugf(l, "Failed to remove named pipe file on Close().")
if err := l.Listener.Close(); err != nil {
return err
return nil