config: add read_very_early_config()
[git/git.git] / trace2 / tr2_dst.c
CommitLineData
ee4512ed
JH
1#include "cache.h"
2#include "trace2/tr2_dst.h"
3
4/*
5 * If a Trace2 target cannot be opened for writing, we should issue a
6 * warning to stderr, but this is very annoying if the target is a pipe
7 * or socket and beyond the user's control -- especially since every
8 * git command (and sub-command) will print the message. So we silently
9 * eat these warnings and just discard the trace data.
10 *
11 * Enable the following environment variable to see these warnings.
12 */
13#define TR2_ENVVAR_DST_DEBUG "GIT_TR2_DST_DEBUG"
14
15static int tr2_dst_want_warning(void)
16{
17 static int tr2env_dst_debug = -1;
18
19 if (tr2env_dst_debug == -1) {
20 const char *env_value = getenv(TR2_ENVVAR_DST_DEBUG);
21 if (!env_value || !*env_value)
22 tr2env_dst_debug = 0;
23 else
24 tr2env_dst_debug = atoi(env_value) > 0;
25 }
26
27 return tr2env_dst_debug;
28}
29
30void tr2_dst_trace_disable(struct tr2_dst *dst)
31{
32 if (dst->need_close)
33 close(dst->fd);
34 dst->fd = 0;
35 dst->initialized = 1;
36 dst->need_close = 0;
37}
38
39static int tr2_dst_try_path(struct tr2_dst *dst, const char *tgt_value)
40{
41 int fd = open(tgt_value, O_WRONLY | O_APPEND | O_CREAT, 0666);
42 if (fd == -1) {
43 if (tr2_dst_want_warning())
44 warning("trace2: could not open '%s' for '%s' tracing: %s",
45 tgt_value, dst->env_var_name, strerror(errno));
46
47 tr2_dst_trace_disable(dst);
48 return 0;
49 }
50
51 dst->fd = fd;
52 dst->need_close = 1;
53 dst->initialized = 1;
54
55 return dst->fd;
56}
57
58#ifndef NO_UNIX_SOCKETS
59#define PREFIX_AF_UNIX "af_unix:"
60#define PREFIX_AF_UNIX_STREAM "af_unix:stream:"
61#define PREFIX_AF_UNIX_DGRAM "af_unix:dgram:"
62
63static int tr2_dst_try_uds_connect(const char *path, int sock_type, int *out_fd)
64{
65 int fd;
66 struct sockaddr_un sa;
67
68 fd = socket(AF_UNIX, sock_type, 0);
69 if (fd == -1)
70 return errno;
71
72 sa.sun_family = AF_UNIX;
73 strlcpy(sa.sun_path, path, sizeof(sa.sun_path));
74
75 if (connect(fd, (struct sockaddr *)&sa, sizeof(sa)) == -1) {
76 int e = errno;
77 close(fd);
78 return e;
79 }
80
81 *out_fd = fd;
82 return 0;
83}
84
85#define TR2_DST_UDS_TRY_STREAM (1 << 0)
86#define TR2_DST_UDS_TRY_DGRAM (1 << 1)
87
88static int tr2_dst_try_unix_domain_socket(struct tr2_dst *dst,
89 const char *tgt_value)
90{
91 unsigned int uds_try = 0;
92 int fd;
93 int e;
94 const char *path = NULL;
95
96 /*
97 * Allow "af_unix:[<type>:]<absolute_path>"
98 *
99 * Trace2 always writes complete individual messages (without
100 * chunking), so we can talk to either DGRAM or STREAM type sockets.
101 *
102 * Allow the user to explicitly request the socket type.
103 *
104 * If they omit the socket type, try one and then the other.
105 */
106
107 if (skip_prefix(tgt_value, PREFIX_AF_UNIX_STREAM, &path))
108 uds_try |= TR2_DST_UDS_TRY_STREAM;
109
110 else if (skip_prefix(tgt_value, PREFIX_AF_UNIX_DGRAM, &path))
111 uds_try |= TR2_DST_UDS_TRY_DGRAM;
112
113 else if (skip_prefix(tgt_value, PREFIX_AF_UNIX, &path))
114 uds_try |= TR2_DST_UDS_TRY_STREAM | TR2_DST_UDS_TRY_DGRAM;
115
116 if (!path || !*path) {
117 if (tr2_dst_want_warning())
118 warning("trace2: invalid AF_UNIX value '%s' for '%s' tracing",
119 tgt_value, dst->env_var_name);
120
121 tr2_dst_trace_disable(dst);
122 return 0;
123 }
124
125 if (!is_absolute_path(path) ||
126 strlen(path) >= sizeof(((struct sockaddr_un *)0)->sun_path)) {
127 if (tr2_dst_want_warning())
128 warning("trace2: invalid AF_UNIX path '%s' for '%s' tracing",
129 path, dst->env_var_name);
130
131 tr2_dst_trace_disable(dst);
132 return 0;
133 }
134
135 if (uds_try & TR2_DST_UDS_TRY_STREAM) {
136 e = tr2_dst_try_uds_connect(path, SOCK_STREAM, &fd);
137 if (!e)
138 goto connected;
139 if (e != EPROTOTYPE)
140 goto error;
141 }
142 if (uds_try & TR2_DST_UDS_TRY_DGRAM) {
143 e = tr2_dst_try_uds_connect(path, SOCK_DGRAM, &fd);
144 if (!e)
145 goto connected;
146 }
147
148error:
149 if (tr2_dst_want_warning())
150 warning("trace2: could not connect to socket '%s' for '%s' tracing: %s",
151 path, dst->env_var_name, strerror(e));
152
153 tr2_dst_trace_disable(dst);
154 return 0;
155
156connected:
157 dst->fd = fd;
158 dst->need_close = 1;
159 dst->initialized = 1;
160
161 return dst->fd;
162}
163#endif
164
165static void tr2_dst_malformed_warning(struct tr2_dst *dst,
166 const char *tgt_value)
167{
168 struct strbuf buf = STRBUF_INIT;
169
170 strbuf_addf(&buf, "trace2: unknown value for '%s': '%s'",
171 dst->env_var_name, tgt_value);
172 warning("%s", buf.buf);
173
174 strbuf_release(&buf);
175}
176
177int tr2_dst_get_trace_fd(struct tr2_dst *dst)
178{
179 const char *tgt_value;
180
181 /* don't open twice */
182 if (dst->initialized)
183 return dst->fd;
184
185 dst->initialized = 1;
186
187 tgt_value = getenv(dst->env_var_name);
188
189 if (!tgt_value || !strcmp(tgt_value, "") || !strcmp(tgt_value, "0") ||
190 !strcasecmp(tgt_value, "false")) {
191 dst->fd = 0;
192 return dst->fd;
193 }
194
195 if (!strcmp(tgt_value, "1") || !strcasecmp(tgt_value, "true")) {
196 dst->fd = STDERR_FILENO;
197 return dst->fd;
198 }
199
200 if (strlen(tgt_value) == 1 && isdigit(*tgt_value)) {
201 dst->fd = atoi(tgt_value);
202 return dst->fd;
203 }
204
205 if (is_absolute_path(tgt_value))
206 return tr2_dst_try_path(dst, tgt_value);
207
208#ifndef NO_UNIX_SOCKETS
209 if (starts_with(tgt_value, PREFIX_AF_UNIX))
210 return tr2_dst_try_unix_domain_socket(dst, tgt_value);
211#endif
212
213 /* Always warn about malformed values. */
214 tr2_dst_malformed_warning(dst, tgt_value);
215 tr2_dst_trace_disable(dst);
216 return 0;
217}
218
219int tr2_dst_trace_want(struct tr2_dst *dst)
220{
221 return !!tr2_dst_get_trace_fd(dst);
222}
223
224void tr2_dst_write_line(struct tr2_dst *dst, struct strbuf *buf_line)
225{
226 int fd = tr2_dst_get_trace_fd(dst);
227
228 strbuf_complete_line(buf_line); /* ensure final NL on buffer */
229
230 /*
231 * We do not use write_in_full() because we do not want
232 * a short-write to try again. We are using O_APPEND mode
233 * files and the kernel handles the atomic seek+write. If
234 * another thread or git process is concurrently writing to
235 * this fd or file, our remainder-write may not be contiguous
236 * with our initial write of this message. And that will
237 * confuse readers. So just don't bother.
238 *
239 * It is assumed that TRACE2 messages are short enough that
240 * the system can write them in 1 attempt and we won't see
241 * a short-write.
242 *
243 * If we get an IO error, just close the trace dst.
244 */
245 if (write(fd, buf_line->buf, buf_line->len) >= 0)
246 return;
247
248 if (tr2_dst_want_warning())
249 warning("unable to write trace to '%s': %s", dst->env_var_name,
250 strerror(errno));
251 tr2_dst_trace_disable(dst);
252}