github.com/nikandfor/tlog@v0.21.3/cmd/tlog/tlogcmd/main.go (about)

     1  package tlogcmd
     2  
     3  import (
     4  	"context"
     5  	"io"
     6  	"io/fs"
     7  	"net"
     8  	"net/http"
     9  	_ "net/http/pprof"
    10  	"os"
    11  	"os/signal"
    12  	"path/filepath"
    13  	"strings"
    14  	"sync"
    15  	"syscall"
    16  	"time"
    17  
    18  	"github.com/fsnotify/fsnotify"
    19  	"github.com/nikandfor/cli"
    20  	"github.com/nikandfor/cli/flag"
    21  	"github.com/nikandfor/errors"
    22  	"github.com/nikandfor/graceful"
    23  	"github.com/nikandfor/hacked/hnet"
    24  
    25  	"github.com/nikandfor/tlog"
    26  	"github.com/nikandfor/tlog/agent"
    27  	"github.com/nikandfor/tlog/ext/tlflag"
    28  	"github.com/nikandfor/tlog/tlio"
    29  	"github.com/nikandfor/tlog/tlwire"
    30  	"github.com/nikandfor/tlog/tlz"
    31  	"github.com/nikandfor/tlog/web"
    32  )
    33  
    34  type (
    35  	filereader struct {
    36  		n string
    37  		f *os.File
    38  	}
    39  
    40  	perrWriter struct {
    41  		io.WriteCloser
    42  	}
    43  
    44  	listenerClose struct {
    45  		net.Listener
    46  		def []io.Closer
    47  	}
    48  )
    49  
    50  func App() *cli.Command {
    51  	catCmd := &cli.Command{
    52  		Name:        "convert,cat,c",
    53  		Description: "read tlog encoded logs",
    54  		Action:      cat,
    55  		Args:        cli.Args{},
    56  		Flags: []*cli.Flag{
    57  			cli.NewFlag("output,out,o", "-?dm", "output file (empty is stderr, - is stdout)"),
    58  			cli.NewFlag("follow,f", false, "wait for changes until terminated"),
    59  			cli.NewFlag("head", 0, "skip all except first n events"),
    60  			cli.NewFlag("tail", 0, "skip all except last n events"),
    61  			//	cli.NewFlag("filter", "", "span filter"),
    62  			//	cli.NewFlag("filter-depth", 0, "span filter max depth"),
    63  		},
    64  	}
    65  
    66  	tlzCmd := &cli.Command{
    67  		Name:        "tlz,eazy",
    68  		Description: "logs compressor/decompressor",
    69  		Flags: []*cli.Flag{
    70  			cli.NewFlag("output,o", "-", "output file (or stdout)"),
    71  		},
    72  		Commands: []*cli.Command{{
    73  			Name:   "compress,c",
    74  			Action: tlzRun,
    75  			Args:   cli.Args{},
    76  			Flags: []*cli.Flag{
    77  				cli.NewFlag("block,b", 1*tlz.MiB, "compression block size"),
    78  				cli.NewFlag("hash-table,ht,h", 16*1024, "hash table size"),
    79  			},
    80  		}, {
    81  			Name:   "decompress,d",
    82  			Action: tlzRun,
    83  			Args:   cli.Args{},
    84  		}, {
    85  			Name:   "dump",
    86  			Action: tlzRun,
    87  			Args:   cli.Args{},
    88  			Flags: []*cli.Flag{
    89  				cli.NewFlag("base", -1, "global offset"),
    90  			},
    91  		}},
    92  	}
    93  
    94  	agentCmd := &cli.Command{
    95  		Name:        "agent,run",
    96  		Description: "run agent",
    97  		Before:      beforeAgent,
    98  		Action:      agentRun,
    99  		Flags: []*cli.Flag{
   100  			cli.NewFlag("db", "db.tlog", "path to logs db"),
   101  
   102  			cli.NewFlag("listen,l", []string(nil), "listen url"),
   103  
   104  			cli.NewFlag("http", ":8000", "http listen address"),
   105  			cli.NewFlag("http-net", "tcp", "http listen network"),
   106  			cli.NewFlag("http-fs", "", "http templates fs"),
   107  
   108  			cli.NewFlag("labels", "service=tlog-agent", "service labels"),
   109  		},
   110  	}
   111  
   112  	app := &cli.Command{
   113  		Name:        "tlog",
   114  		Description: "tlog cli",
   115  		Before:      before,
   116  		Flags: []*cli.Flag{
   117  			cli.NewFlag("log", "stderr?dm", "log output file (or stderr)"),
   118  			cli.NewFlag("verbosity,v", "", "logger verbosity topics"),
   119  			cli.NewFlag("debug", "", "debug address", flag.Hidden),
   120  			cli.FlagfileFlag,
   121  			cli.HelpFlag,
   122  		},
   123  		Commands: []*cli.Command{
   124  			agentCmd,
   125  			catCmd,
   126  			tlzCmd,
   127  			{
   128  				Name:        "ticker",
   129  				Description: "simple test app that prints current time once in an interval",
   130  				Action:      ticker,
   131  				Flags: []*cli.Flag{
   132  					cli.NewFlag("output,o", "-", "output file (or stdout)"),
   133  					cli.NewFlag("interval,int,i", time.Second, "interval to tick on"),
   134  					cli.NewFlag("labels", "service=ticker,_pid", "labels"),
   135  				},
   136  			},
   137  			{
   138  				Name:   "test",
   139  				Action: test,
   140  				Args:   cli.Args{},
   141  				Hidden: true,
   142  			},
   143  		},
   144  	}
   145  
   146  	return app
   147  }
   148  
   149  func SubApp() *cli.Command {
   150  	app := App()
   151  
   152  	app.Before = nil
   153  	app.After = nil
   154  	app.Flags = nil
   155  
   156  	return app
   157  }
   158  
   159  func before(c *cli.Command) error {
   160  	w, err := tlflag.OpenWriter(c.String("log"))
   161  	if err != nil {
   162  		return errors.Wrap(err, "open log file")
   163  	}
   164  
   165  	tlog.DefaultLogger = tlog.New(w)
   166  
   167  	tlog.SetVerbosity(c.String("verbosity"))
   168  
   169  	if q := c.String("debug"); q != "" {
   170  		l, err := net.Listen("tcp", q)
   171  		if err != nil {
   172  			return errors.Wrap(err, "listen debug")
   173  		}
   174  
   175  		go func() {
   176  			tlog.Printw("start debug interface", "addr", l.Addr())
   177  
   178  			err := http.Serve(l, nil)
   179  			if err != nil {
   180  				tlog.Printw("debug", "addr", q, "err", err, "", tlog.Fatal)
   181  				panic(err)
   182  			}
   183  		}()
   184  	}
   185  
   186  	if tlog.If("dump_file_reader") {
   187  		tlflag.OpenFileReader = tlflag.OSOpenFile
   188  		tlflag.OpenFileReader = tlflag.OpenFileDumpReader(tlflag.OpenFileReader)
   189  		tlflag.OpenFileReader = tlflag.OpenFileReReader(tlflag.OpenFileReader)
   190  	}
   191  
   192  	return nil
   193  }
   194  
   195  func beforeAgent(c *cli.Command) error {
   196  	if f := c.Flag("labels"); f != nil {
   197  		if ls, ok := f.Value.(string); ok {
   198  			tlog.SetLabels(tlog.ParseLabels(ls)...)
   199  		}
   200  	}
   201  
   202  	return nil
   203  }
   204  
   205  func agentRun(c *cli.Command) (err error) {
   206  	ctx := context.Background()
   207  	ctx = tlog.ContextWithSpan(ctx, tlog.Root())
   208  
   209  	a, err := agent.New(c.String("db"))
   210  	if err != nil {
   211  		return errors.Wrap(err, "new agent")
   212  	}
   213  
   214  	group := graceful.New()
   215  
   216  	if q := c.String("http"); q != "" {
   217  		l, err := net.Listen(c.String("http-net"), q)
   218  		if err != nil {
   219  			return errors.Wrap(err, "listen http")
   220  		}
   221  
   222  		s, err := web.New(a)
   223  		if err != nil {
   224  			return errors.Wrap(err, "new web server")
   225  		}
   226  
   227  		if q := c.String("http-fs"); q != "" {
   228  			s.FS = http.Dir(q)
   229  		}
   230  
   231  		group.Add(func(ctx context.Context) (err error) {
   232  			tr := tlog.SpawnFromContext(ctx, "web_server", "addr", l.Addr())
   233  			defer tr.Finish("err", &err)
   234  
   235  			ctx = tlog.ContextWithSpan(ctx, tr)
   236  
   237  			err = s.Serve(ctx, l, func(ctx context.Context, c net.Conn) (err error) {
   238  				tr, ctx := tlog.SpawnFromContextAndWrap(ctx, "web_request", "remote_addr", c.RemoteAddr(), "local_addr", c.LocalAddr())
   239  				defer tr.Finish("err", &err)
   240  
   241  				return s.HandleConn(ctx, c)
   242  			})
   243  			if errors.Is(err, context.Canceled) {
   244  				err = nil
   245  			}
   246  
   247  			return errors.Wrap(err, "serve http")
   248  		})
   249  	}
   250  
   251  	for _, lurl := range c.Flag("listen").Value.([]string) {
   252  		u, err := tlflag.ParseURL(lurl)
   253  		if err != nil {
   254  			return errors.Wrap(err, "parse %v", lurl)
   255  		}
   256  
   257  		tlog.Printw("listen", "scheme", u.Scheme, "host", u.Host, "path", u.Path, "query", u.RawQuery)
   258  
   259  		host := u.Host
   260  		if u.Scheme == "unix" || u.Scheme == "unixgram" {
   261  			host = u.Path
   262  		}
   263  
   264  		l, p, err := listen(u.Scheme, host)
   265  		if err != nil {
   266  			return errors.Wrap(err, "listen %v", host)
   267  		}
   268  
   269  		switch {
   270  		case u.Scheme == "unix", u.Scheme == "tcp":
   271  			group.Add(func(ctx context.Context) error {
   272  				var wg sync.WaitGroup
   273  
   274  				defer wg.Wait()
   275  
   276  				for {
   277  					c, err := hnet.Accept(ctx, l)
   278  					if errors.Is(err, context.Canceled) {
   279  						return nil
   280  					}
   281  					if err != nil {
   282  						return errors.Wrap(err, "accept")
   283  					}
   284  
   285  					wg.Add(1)
   286  
   287  					tr := tlog.SpawnFromContext(ctx, "agent_writer", "local_addr", c.LocalAddr(), "remote_addr", c.RemoteAddr())
   288  
   289  					go func() {
   290  						defer wg.Done()
   291  						defer tr.Finish()
   292  						defer c.Close()
   293  
   294  						rr := tlwire.NewStreamDecoder(c)
   295  
   296  						_, _ = rr.WriteTo(a)
   297  					}()
   298  				}
   299  			}, graceful.WithStop(func(ctx context.Context) error {
   300  				return l.Close()
   301  			}))
   302  		case u.Scheme == "unixgram", u.Scheme == "udp":
   303  			group.Add(func(ctx context.Context) error {
   304  				buf := make([]byte, 0x1000)
   305  
   306  				for {
   307  					n, _, err := hnet.ReadFrom(ctx, p, buf)
   308  					if err != nil {
   309  						return errors.Wrap(err, "read")
   310  					}
   311  
   312  					_, _ = a.Write(buf[:n])
   313  				}
   314  			})
   315  		default:
   316  			return errors.New("unsupported listener: %v", u.Scheme)
   317  		}
   318  	}
   319  
   320  	return group.Run(ctx, graceful.IgnoreErrors(context.Canceled))
   321  }
   322  
   323  func cat(c *cli.Command) (err error) {
   324  	w, err := tlflag.OpenWriter(c.String("out"))
   325  	if err != nil {
   326  		return err
   327  	}
   328  
   329  	defer func() {
   330  		e := w.Close()
   331  		if err == nil {
   332  			err = e
   333  		}
   334  	}()
   335  
   336  	var fs *fsnotify.Watcher //nolint:gocritic
   337  
   338  	if c.Bool("follow") {
   339  		fs, err = fsnotify.NewWatcher()
   340  		if err != nil {
   341  			return errors.Wrap(err, "create fs watcher")
   342  		}
   343  
   344  		defer func() {
   345  			e := fs.Close()
   346  			if err == nil {
   347  				err = errors.Wrap(e, "close watcher")
   348  			}
   349  		}()
   350  	}
   351  
   352  	rs := make(map[string]io.WriterTo, c.Args.Len())
   353  	defer func() {
   354  		for name, r := range rs {
   355  			tlio.CloseWrap(r, name, &err)
   356  		}
   357  	}()
   358  
   359  	var addFile func(a string) error
   360  	addFile = func(a string) (err error) {
   361  		a = filepath.Clean(a)
   362  
   363  		inf, err := os.Stat(a)
   364  		if err != nil {
   365  			return errors.Wrap(err, "stat %v", a)
   366  		}
   367  
   368  		if fs != nil {
   369  			err = fs.Add(a)
   370  			tlog.V("watch").Printw("watch file", "name", a, "err", err)
   371  			if err != nil {
   372  				return errors.Wrap(err, "watch")
   373  			}
   374  		}
   375  
   376  		if inf.IsDir() {
   377  			files, err := os.ReadDir(a)
   378  			if err != nil {
   379  				return errors.Wrap(err, "readdir %v", a)
   380  			}
   381  
   382  			for _, f := range files {
   383  				if strings.HasPrefix(f.Name(), ".") {
   384  					continue
   385  				}
   386  				if !f.Type().IsRegular() {
   387  					continue
   388  				}
   389  
   390  				err = addFile(filepath.Join(a, f.Name()))
   391  				if err != nil {
   392  					return err
   393  				}
   394  			}
   395  
   396  			return nil
   397  		}
   398  
   399  		var rc io.ReadCloser
   400  
   401  		rc, err = tlflag.OpenReader(a)
   402  		if err != nil {
   403  			return errors.Wrap(err, "open: %v", a)
   404  		}
   405  
   406  		rs[a] = tlwire.NewStreamDecoder(rc)
   407  
   408  		var w0 io.Writer = w
   409  
   410  		if f := c.Flag("tail"); f.IsSet {
   411  			w0 = tlio.NewTailWriter(w0, f.Value.(int))
   412  		}
   413  
   414  		if f := c.Flag("head"); f.IsSet {
   415  			fl, _ := w0.(tlio.Flusher)
   416  
   417  			w0 = tlio.NewHeadWriter(w0, f.Value.(int))
   418  
   419  			if _, ok := w0.(tlio.Flusher); !ok && fl != nil {
   420  				w0 = tlio.WriteFlusher{
   421  					Writer:  w0,
   422  					Flusher: fl,
   423  				}
   424  			}
   425  		}
   426  
   427  		_, err = rs[a].WriteTo(w0)
   428  		if errors.Is(err, io.EOF) {
   429  			err = nil
   430  		}
   431  
   432  		if f, ok := w0.(tlio.Flusher); ok {
   433  			e := f.Flush()
   434  			if err == nil {
   435  				err = errors.Wrap(e, "flush: %v", a)
   436  			}
   437  		}
   438  
   439  		if err != nil {
   440  			return errors.Wrap(err, "copy: %v", a)
   441  		}
   442  
   443  		return nil
   444  	}
   445  
   446  	for _, a := range c.Args {
   447  		err = addFile(a)
   448  		if err != nil {
   449  			return err
   450  		}
   451  	}
   452  
   453  	if !c.Bool("follow") {
   454  		return nil
   455  	}
   456  
   457  	sigc := make(chan os.Signal, 3)
   458  	signal.Notify(sigc, os.Interrupt)
   459  
   460  	var ev fsnotify.Event
   461  	for {
   462  		select {
   463  		case ev = <-fs.Events:
   464  		case <-sigc:
   465  			return nil
   466  		case err = <-fs.Errors:
   467  			return errors.Wrap(err, "watch")
   468  		}
   469  
   470  		tlog.V("fsevent").Printw("fs event", "name", ev.Name, "op", ev.Op)
   471  
   472  		switch {
   473  		case ev.Op&fsnotify.Create != 0:
   474  			err = addFile(ev.Name)
   475  			if err != nil {
   476  				return errors.Wrap(err, "add created")
   477  			}
   478  		case ev.Op&fsnotify.Remove != 0:
   479  		//	err = fs.Remove(ev.Name)
   480  		//	if err != nil {
   481  		//		return errors.Wrap(err, "remove watch")
   482  		//	}
   483  		case ev.Op&fsnotify.Write != 0:
   484  			r, ok := rs[ev.Name]
   485  			if !ok {
   486  				return errors.New("unexpected event: %v (%v)", ev.Name, rs)
   487  			}
   488  
   489  			_, err = r.WriteTo(w)
   490  			switch {
   491  			case errors.Is(err, io.EOF):
   492  			case errors.Is(err, io.ErrUnexpectedEOF):
   493  				tlog.V("unexpected_eof").Printw("unexpected EOF", "file", ev.Name)
   494  			case err != nil:
   495  				return errors.Wrap(err, "copy: %v", ev.Name)
   496  			}
   497  		}
   498  	}
   499  }
   500  
   501  func tlzRun(c *cli.Command) (err error) {
   502  	var rs []io.Reader
   503  	for _, a := range c.Args {
   504  		if a == "-" {
   505  			rs = append(rs, os.Stdin)
   506  		} else {
   507  			rs = append(rs, &filereader{n: a})
   508  		}
   509  	}
   510  
   511  	if len(rs) == 0 {
   512  		rs = append(rs, os.Stdin)
   513  	}
   514  
   515  	var w io.Writer
   516  	if q := c.String("output"); q == "" || q == "-" {
   517  		w = os.Stdout
   518  	} else {
   519  		f, err := os.Create(q)
   520  		if err != nil {
   521  			return errors.Wrap(err, "open output")
   522  		}
   523  		defer func() {
   524  			e := f.Close()
   525  			if err == nil {
   526  				err = e
   527  			}
   528  		}()
   529  
   530  		w = f
   531  	}
   532  
   533  	switch c.MainName() {
   534  	case "compress":
   535  		e := tlz.NewEncoder(w, c.Int("block"))
   536  
   537  		for _, r := range rs {
   538  			_, err = io.Copy(e, r)
   539  			if err != nil {
   540  				return errors.Wrap(err, "copy")
   541  			}
   542  		}
   543  	case "decompress":
   544  		d := tlz.NewDecoder(io.MultiReader(rs...))
   545  
   546  		_, err = io.Copy(w, d)
   547  		if err != nil {
   548  			return errors.Wrap(err, "copy")
   549  		}
   550  	case "dump":
   551  		d := tlz.NewDumper(w) // BUG: dumper does not work with writes not aligned to tags
   552  
   553  		d.GlobalOffset = int64(c.Int("base"))
   554  
   555  		data, err := io.ReadAll(io.MultiReader(rs...))
   556  		if err != nil {
   557  			return errors.Wrap(err, "read all")
   558  		}
   559  
   560  		_, err = d.Write(data)
   561  		if err != nil {
   562  			return errors.Wrap(err, "copy")
   563  		}
   564  	default:
   565  		return errors.New("unexpected command: %v", c.MainName())
   566  	}
   567  
   568  	return nil
   569  }
   570  
   571  func ticker(c *cli.Command) error {
   572  	w, err := tlflag.OpenWriter(c.String("output"))
   573  	if err != nil {
   574  		return errors.Wrap(err, "open output")
   575  	}
   576  
   577  	if tlog.If("output") {
   578  		tlflag.DumpWriter(tlog.Root(), w)
   579  	}
   580  
   581  	w = perrWriter{
   582  		WriteCloser: w,
   583  	}
   584  
   585  	l := tlog.New(w)
   586  
   587  	t := time.NewTicker(c.Duration("interval"))
   588  	defer t.Stop()
   589  
   590  	ls := tlog.ParseLabels(c.String("labels"))
   591  
   592  	l.SetLabels(ls...)
   593  
   594  	var first time.Time
   595  	dur := c.Duration("interval")
   596  	drift := 0.
   597  	i := 0
   598  
   599  	const alpha = 0.0001
   600  
   601  	for t := range t.C {
   602  		if i == 0 {
   603  			first = t
   604  		}
   605  
   606  		diff := t.Sub(first) - time.Duration(i)*dur
   607  		drift := drift*(1-alpha) + float64(diff)*alpha
   608  
   609  		l.Printw("tick", "i", i, "time", t, "diff", diff, "drift", time.Duration(drift))
   610  
   611  		i++
   612  	}
   613  
   614  	return nil
   615  }
   616  
   617  func test(c *cli.Command) error {
   618  	return nil
   619  }
   620  
   621  func (f *filereader) Read(p []byte) (n int, err error) {
   622  	if f.f == nil {
   623  		f.f, err = os.Open(f.n)
   624  		if err != nil {
   625  			return 0, errors.Wrap(err, "open %v", f.n)
   626  		}
   627  	}
   628  
   629  	n, err = f.f.Read(p)
   630  
   631  	if err != nil {
   632  		_ = f.f.Close()
   633  	}
   634  
   635  	return
   636  }
   637  
   638  func (w perrWriter) Write(p []byte) (n int, err error) {
   639  	n, err = w.WriteCloser.Write(p)
   640  
   641  	if err != nil {
   642  		tlog.Printw("write", "err", err)
   643  	}
   644  
   645  	return
   646  }
   647  
   648  func (w perrWriter) Close() (err error) {
   649  	err = w.WriteCloser.Close()
   650  
   651  	if err != nil {
   652  		tlog.Printw("close", "err", err)
   653  	}
   654  
   655  	return
   656  }
   657  
   658  func isDir(name string) bool {
   659  	inf, err := os.Stat(name)
   660  	if err != nil {
   661  		return false
   662  	}
   663  
   664  	return inf.IsDir()
   665  }
   666  
   667  func isFifo(name string) bool {
   668  	inf, err := os.Stat(name)
   669  	if err != nil {
   670  		return false
   671  	}
   672  
   673  	mode := inf.Mode()
   674  
   675  	return mode&fs.ModeNamedPipe != 0
   676  }
   677  
   678  func listen(netw, addr string) (l net.Listener, p net.PacketConn, err error) {
   679  	switch netw {
   680  	case "unix", "unixgram":
   681  		_ = os.Remove(addr)
   682  	}
   683  
   684  	switch netw {
   685  	case "tcp", "unix":
   686  		l, err = net.Listen(netw, addr)
   687  		if err != nil {
   688  			return nil, nil, errors.Wrap(err, "listen")
   689  		}
   690  
   691  		switch l := l.(type) {
   692  		case *net.UnixListener:
   693  			l.SetUnlinkOnClose(true)
   694  		default:
   695  			return nil, nil, errors.New("unsupported listener type: %T", l)
   696  		}
   697  	case "udp", "unixgram":
   698  		p, err = net.ListenPacket(netw, addr)
   699  		if err != nil {
   700  			return nil, nil, errors.Wrap(err, "listen packet")
   701  		}
   702  	default:
   703  		return nil, nil, errors.New("unsupported network type: %v", netw)
   704  	}
   705  
   706  	return l, p, nil
   707  }
   708  
   709  func listen0(netw, addr string) (l net.Listener, err error) {
   710  	unix := strings.HasPrefix(netw, "unix")
   711  
   712  	var def []io.Closer
   713  
   714  	if unix {
   715  		cl, err := flockLock(addr + ".lock")
   716  		if err != nil {
   717  			return nil, errors.Wrap(err, "lock")
   718  		}
   719  
   720  		def = append(def, cl)
   721  
   722  		err = os.Remove(addr)
   723  		if err != nil && !os.IsNotExist(err) {
   724  			return nil, errors.Wrap(err, "remove old socket file")
   725  		}
   726  	}
   727  
   728  	l, err = net.Listen(netw, addr)
   729  	if err != nil {
   730  		return nil, errors.Wrap(err, "listen: %v", addr)
   731  	}
   732  
   733  	tlog.Printw("listen", "net", netw, "addr", addr, "l_type", tlog.NextAsType, l)
   734  
   735  	// unix listener removed by close
   736  
   737  	if def != nil {
   738  		return listenerClose{
   739  			Listener: l,
   740  			def:      def,
   741  		}, nil
   742  	}
   743  
   744  	return l, nil
   745  }
   746  
   747  func flockLock(addr string) (_ io.Closer, err error) {
   748  	lock, err := os.OpenFile(addr, os.O_CREATE, 0o644)
   749  	if err != nil {
   750  		return nil, errors.Wrap(err, "open lock")
   751  	}
   752  
   753  	err = syscall.Flock(int(lock.Fd()), syscall.LOCK_EX|syscall.LOCK_NB)
   754  	if err != nil {
   755  		return nil, errors.Wrap(err, "flock")
   756  	}
   757  
   758  	cl := func() (err error) {
   759  		err = lock.Close()
   760  		if err != nil {
   761  			return errors.Wrap(err, "close lock")
   762  		}
   763  
   764  		err = os.Remove(addr)
   765  		if err != nil {
   766  			return errors.Wrap(err, "remove lock")
   767  		}
   768  
   769  		return nil
   770  	}
   771  
   772  	return tlio.CloserFunc(cl), nil
   773  }
   774  
   775  func (p listenerClose) SetDeadline(t time.Time) error {
   776  	return p.Listener.(interface{ SetDeadline(time.Time) error }).SetDeadline(t)
   777  }
   778  
   779  func (p listenerClose) Close() (err error) {
   780  	err = p.Listener.Close()
   781  
   782  	for i := len(p.def) - 1; i >= 0; i-- {
   783  		e := p.def[i].Close()
   784  		if err == nil {
   785  			err = e
   786  		}
   787  	}
   788  
   789  	return
   790  }
   791  
   792  func closeIfErr(c io.Closer, errp *error) {
   793  	if *errp == nil {
   794  		return
   795  	}
   796  
   797  	_ = c.Close()
   798  }