2 #include "trace2/tr2_dst.h"
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.
11 * Enable the following environment variable to see these warnings.
13 #define TR2_ENVVAR_DST_DEBUG "GIT_TR2_DST_DEBUG"
15 static int tr2_dst_want_warning(void)
17 static int tr2env_dst_debug
= -1;
19 if (tr2env_dst_debug
== -1) {
20 const char *env_value
= getenv(TR2_ENVVAR_DST_DEBUG
);
21 if (!env_value
|| !*env_value
)
24 tr2env_dst_debug
= atoi(env_value
) > 0;
27 return tr2env_dst_debug
;
30 void tr2_dst_trace_disable(struct tr2_dst
*dst
)
39 static int tr2_dst_try_path(struct tr2_dst
*dst
, const char *tgt_value
)
41 int fd
= open(tgt_value
, O_WRONLY
| O_APPEND
| O_CREAT
, 0666);
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
));
47 tr2_dst_trace_disable(dst
);
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:"
63 static int tr2_dst_try_uds_connect(const char *path
, int sock_type
, int *out_fd
)
66 struct sockaddr_un sa
;
68 fd
= socket(AF_UNIX
, sock_type
, 0);
72 sa
.sun_family
= AF_UNIX
;
73 strlcpy(sa
.sun_path
, path
, sizeof(sa
.sun_path
));
75 if (connect(fd
, (struct sockaddr
*)&sa
, sizeof(sa
)) == -1) {
85 #define TR2_DST_UDS_TRY_STREAM (1 << 0)
86 #define TR2_DST_UDS_TRY_DGRAM (1 << 1)
88 static int tr2_dst_try_unix_domain_socket(struct tr2_dst
*dst
,
89 const char *tgt_value
)
91 unsigned int uds_try
= 0;
94 const char *path
= NULL
;
97 * Allow "af_unix:[<type>:]<absolute_path>"
99 * Trace2 always writes complete individual messages (without
100 * chunking), so we can talk to either DGRAM or STREAM type sockets.
102 * Allow the user to explicitly request the socket type.
104 * If they omit the socket type, try one and then the other.
107 if (skip_prefix(tgt_value
, PREFIX_AF_UNIX_STREAM
, &path
))
108 uds_try
|= TR2_DST_UDS_TRY_STREAM
;
110 else if (skip_prefix(tgt_value
, PREFIX_AF_UNIX_DGRAM
, &path
))
111 uds_try
|= TR2_DST_UDS_TRY_DGRAM
;
113 else if (skip_prefix(tgt_value
, PREFIX_AF_UNIX
, &path
))
114 uds_try
|= TR2_DST_UDS_TRY_STREAM
| TR2_DST_UDS_TRY_DGRAM
;
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
);
121 tr2_dst_trace_disable(dst
);
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
);
131 tr2_dst_trace_disable(dst
);
135 if (uds_try
& TR2_DST_UDS_TRY_STREAM
) {
136 e
= tr2_dst_try_uds_connect(path
, SOCK_STREAM
, &fd
);
142 if (uds_try
& TR2_DST_UDS_TRY_DGRAM
) {
143 e
= tr2_dst_try_uds_connect(path
, SOCK_DGRAM
, &fd
);
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
));
153 tr2_dst_trace_disable(dst
);
159 dst
->initialized
= 1;
165 static void tr2_dst_malformed_warning(struct tr2_dst
*dst
,
166 const char *tgt_value
)
168 struct strbuf buf
= STRBUF_INIT
;
170 strbuf_addf(&buf
, "trace2: unknown value for '%s': '%s'",
171 dst
->env_var_name
, tgt_value
);
172 warning("%s", buf
.buf
);
174 strbuf_release(&buf
);
177 int tr2_dst_get_trace_fd(struct tr2_dst
*dst
)
179 const char *tgt_value
;
181 /* don't open twice */
182 if (dst
->initialized
)
185 dst
->initialized
= 1;
187 tgt_value
= getenv(dst
->env_var_name
);
189 if (!tgt_value
|| !strcmp(tgt_value
, "") || !strcmp(tgt_value
, "0") ||
190 !strcasecmp(tgt_value
, "false")) {
195 if (!strcmp(tgt_value
, "1") || !strcasecmp(tgt_value
, "true")) {
196 dst
->fd
= STDERR_FILENO
;
200 if (strlen(tgt_value
) == 1 && isdigit(*tgt_value
)) {
201 dst
->fd
= atoi(tgt_value
);
205 if (is_absolute_path(tgt_value
))
206 return tr2_dst_try_path(dst
, tgt_value
);
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
);
213 /* Always warn about malformed values. */
214 tr2_dst_malformed_warning(dst
, tgt_value
);
215 tr2_dst_trace_disable(dst
);
219 int tr2_dst_trace_want(struct tr2_dst
*dst
)
221 return !!tr2_dst_get_trace_fd(dst
);
224 void tr2_dst_write_line(struct tr2_dst
*dst
, struct strbuf
*buf_line
)
226 int fd
= tr2_dst_get_trace_fd(dst
);
228 strbuf_complete_line(buf_line
); /* ensure final NL on buffer */
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.
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
243 * If we get an IO error, just close the trace dst.
245 if (write(fd
, buf_line
->buf
, buf_line
->len
) >= 0)
248 if (tr2_dst_want_warning())
249 warning("unable to write trace to '%s': %s", dst
->env_var_name
,
251 tr2_dst_trace_disable(dst
);