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