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