1package logger // import "github.com/docker/docker/daemon/logger"
2
3import (
4	"fmt"
5	"io"
6	"os"
7	"path/filepath"
8
9	"github.com/docker/docker/api/types/plugins/logdriver"
10	"github.com/docker/docker/errdefs"
11	getter "github.com/docker/docker/pkg/plugingetter"
12	"github.com/docker/docker/pkg/plugins"
13	"github.com/docker/docker/pkg/stringid"
14	"github.com/pkg/errors"
15)
16
17var pluginGetter getter.PluginGetter
18
19const extName = "LogDriver"
20
21// logPlugin defines the available functions that logging plugins must implement.
22type logPlugin interface {
23	StartLogging(streamPath string, info Info) (err error)
24	StopLogging(streamPath string) (err error)
25	Capabilities() (cap Capability, err error)
26	ReadLogs(info Info, config ReadConfig) (stream io.ReadCloser, err error)
27}
28
29// RegisterPluginGetter sets the plugingetter
30func RegisterPluginGetter(plugingetter getter.PluginGetter) {
31	pluginGetter = plugingetter
32}
33
34// GetDriver returns a logging driver by its name.
35// If the driver is empty, it looks for the local driver.
36func getPlugin(name string, mode int) (Creator, error) {
37	p, err := pluginGetter.Get(name, extName, mode)
38	if err != nil {
39		return nil, fmt.Errorf("error looking up logging plugin %s: %v", name, err)
40	}
41
42	client, err := makePluginClient(p)
43	if err != nil {
44		return nil, err
45	}
46	return makePluginCreator(name, client, p.ScopedPath), nil
47}
48
49func makePluginClient(p getter.CompatPlugin) (logPlugin, error) {
50	if pc, ok := p.(getter.PluginWithV1Client); ok {
51		return &logPluginProxy{pc.Client()}, nil
52	}
53	pa, ok := p.(getter.PluginAddr)
54	if !ok {
55		return nil, errdefs.System(errors.Errorf("got unknown plugin type %T", p))
56	}
57
58	if pa.Protocol() != plugins.ProtocolSchemeHTTPV1 {
59		return nil, errors.Errorf("plugin protocol not supported: %s", p)
60	}
61
62	addr := pa.Addr()
63	c, err := plugins.NewClientWithTimeout(addr.Network()+"://"+addr.String(), nil, pa.Timeout())
64	if err != nil {
65		return nil, errors.Wrap(err, "error making plugin client")
66	}
67	return &logPluginProxy{c}, nil
68}
69
70func makePluginCreator(name string, l logPlugin, scopePath func(s string) string) Creator {
71	return func(logCtx Info) (logger Logger, err error) {
72		defer func() {
73			if err != nil {
74				pluginGetter.Get(name, extName, getter.Release)
75			}
76		}()
77
78		unscopedPath := filepath.Join("/", "run", "docker", "logging")
79		logRoot := scopePath(unscopedPath)
80		if err := os.MkdirAll(logRoot, 0700); err != nil {
81			return nil, err
82		}
83
84		id := stringid.GenerateRandomID()
85		a := &pluginAdapter{
86			driverName: name,
87			id:         id,
88			plugin:     l,
89			fifoPath:   filepath.Join(logRoot, id),
90			logInfo:    logCtx,
91		}
92
93		cap, err := a.plugin.Capabilities()
94		if err == nil {
95			a.capabilities = cap
96		}
97
98		stream, err := openPluginStream(a)
99		if err != nil {
100			return nil, err
101		}
102
103		a.stream = stream
104		a.enc = logdriver.NewLogEntryEncoder(a.stream)
105
106		if err := l.StartLogging(filepath.Join(unscopedPath, id), logCtx); err != nil {
107			return nil, errors.Wrapf(err, "error creating logger")
108		}
109
110		if cap.ReadLogs {
111			return &pluginAdapterWithRead{a}, nil
112		}
113
114		return a, nil
115	}
116}
117