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