1
#include "git-compat-util.h"
5
#include "trace2/tr2_dst.h"
6
#include "trace2/tr2_sid.h"
7
#include "trace2/tr2_sysenv.h"
12
#define MAX_AUTO_ATTEMPTS 10
18
#define DISCARD_SENTINEL_NAME "git-trace2-discard"
25
static int tr2env_max_files = 0;
27
static int tr2_dst_want_warning(void)
29
static int tr2env_dst_debug = -1;
31
if (tr2env_dst_debug == -1) {
32
const char *env_value = tr2_sysenv_get(TR2_SYSENV_DST_DEBUG);
33
if (!env_value || !*env_value)
36
tr2env_dst_debug = atoi(env_value) > 0;
39
return tr2env_dst_debug;
42
void tr2_dst_trace_disable(struct tr2_dst *dst)
66
static int tr2_dst_too_many_files(struct tr2_dst *dst, const char *tgt_prefix)
68
int file_count = 0, max_files = 0, ret = 0;
69
const char *max_files_var;
71
struct strbuf path = STRBUF_INIT, sentinel_path = STRBUF_INIT;
75
max_files_var = tr2_sysenv_get(TR2_SYSENV_MAX_FILES);
76
if (max_files_var && *max_files_var && ((max_files = atoi(max_files_var)) >= 0))
77
tr2env_max_files = max_files;
79
if (!tr2env_max_files) {
84
strbuf_addstr(&path, tgt_prefix);
85
if (!is_dir_sep(path.buf[path.len - 1])) {
86
strbuf_addch(&path, '/');
90
strbuf_addbuf(&sentinel_path, &path);
91
strbuf_addstr(&sentinel_path, DISCARD_SENTINEL_NAME);
92
if (!stat(sentinel_path.buf, &statbuf)) {
98
dirp = opendir(path.buf);
99
while (file_count < tr2env_max_files && dirp && readdir(dirp))
104
if (file_count >= tr2env_max_files) {
105
dst->too_many_files = 1;
106
dst->fd = open(sentinel_path.buf, O_WRONLY | O_CREAT | O_EXCL, 0666);
112
strbuf_release(&path);
113
strbuf_release(&sentinel_path);
117
static int tr2_dst_try_auto_path(struct tr2_dst *dst, const char *tgt_prefix)
120
const char *last_slash, *sid = tr2_sid_get();
121
struct strbuf path = STRBUF_INIT;
122
size_t base_path_len;
123
unsigned attempt_count;
125
last_slash = strrchr(sid, '/');
127
sid = last_slash + 1;
129
strbuf_addstr(&path, tgt_prefix);
130
if (!is_dir_sep(path.buf[path.len - 1]))
131
strbuf_addch(&path, '/');
132
strbuf_addstr(&path, sid);
133
base_path_len = path.len;
135
too_many_files = tr2_dst_too_many_files(dst, tgt_prefix);
136
if (!too_many_files) {
137
for (attempt_count = 0; attempt_count < MAX_AUTO_ATTEMPTS; attempt_count++) {
138
if (attempt_count > 0) {
139
strbuf_setlen(&path, base_path_len);
140
strbuf_addf(&path, ".%d", attempt_count);
143
dst->fd = open(path.buf, O_WRONLY | O_CREAT | O_EXCL, 0666);
147
} else if (too_many_files == 1) {
148
strbuf_release(&path);
149
if (tr2_dst_want_warning())
150
warning("trace2: not opening %s trace file due to too "
151
"many files in target directory %s",
152
tr2_sysenv_display_name(dst->sysenv_var),
158
if (tr2_dst_want_warning())
159
warning("trace2: could not open '%.*s' for '%s' tracing: %s",
160
(int) base_path_len, path.buf,
161
tr2_sysenv_display_name(dst->sysenv_var),
164
tr2_dst_trace_disable(dst);
165
strbuf_release(&path);
169
strbuf_release(&path);
172
dst->initialized = 1;
177
static int tr2_dst_try_path(struct tr2_dst *dst, const char *tgt_value)
179
int fd = open(tgt_value, O_WRONLY | O_APPEND | O_CREAT, 0666);
181
if (tr2_dst_want_warning())
182
warning("trace2: could not open '%s' for '%s' tracing: %s",
184
tr2_sysenv_display_name(dst->sysenv_var),
187
tr2_dst_trace_disable(dst);
193
dst->initialized = 1;
198
#ifndef NO_UNIX_SOCKETS
199
#define PREFIX_AF_UNIX "af_unix:"
200
#define PREFIX_AF_UNIX_STREAM "af_unix:stream:"
201
#define PREFIX_AF_UNIX_DGRAM "af_unix:dgram:"
203
static int tr2_dst_try_uds_connect(const char *path, int sock_type, int *out_fd)
206
struct sockaddr_un sa;
208
fd = socket(AF_UNIX, sock_type, 0);
212
sa.sun_family = AF_UNIX;
213
strlcpy(sa.sun_path, path, sizeof(sa.sun_path));
215
if (connect(fd, (struct sockaddr *)&sa, sizeof(sa)) == -1) {
216
int saved_errno = errno;
226
#define TR2_DST_UDS_TRY_STREAM (1 << 0)
227
#define TR2_DST_UDS_TRY_DGRAM (1 << 1)
229
static int tr2_dst_try_unix_domain_socket(struct tr2_dst *dst,
230
const char *tgt_value)
232
unsigned int uds_try = 0;
234
const char *path = NULL;
247
if (skip_prefix(tgt_value, PREFIX_AF_UNIX_STREAM, &path))
248
uds_try |= TR2_DST_UDS_TRY_STREAM;
250
else if (skip_prefix(tgt_value, PREFIX_AF_UNIX_DGRAM, &path))
251
uds_try |= TR2_DST_UDS_TRY_DGRAM;
253
else if (skip_prefix(tgt_value, PREFIX_AF_UNIX, &path))
254
uds_try |= TR2_DST_UDS_TRY_STREAM | TR2_DST_UDS_TRY_DGRAM;
256
if (!path || !*path) {
257
if (tr2_dst_want_warning())
258
warning("trace2: invalid AF_UNIX value '%s' for '%s' tracing",
260
tr2_sysenv_display_name(dst->sysenv_var));
262
tr2_dst_trace_disable(dst);
266
if (!is_absolute_path(path) ||
267
strlen(path) >= sizeof(((struct sockaddr_un *)0)->sun_path)) {
268
if (tr2_dst_want_warning())
269
warning("trace2: invalid AF_UNIX path '%s' for '%s' tracing",
270
path, tr2_sysenv_display_name(dst->sysenv_var));
272
tr2_dst_trace_disable(dst);
276
if (uds_try & TR2_DST_UDS_TRY_STREAM) {
277
if (!tr2_dst_try_uds_connect(path, SOCK_STREAM, &fd))
279
if (errno != EPROTOTYPE)
282
if (uds_try & TR2_DST_UDS_TRY_DGRAM) {
283
if (!tr2_dst_try_uds_connect(path, SOCK_DGRAM, &fd))
288
if (tr2_dst_want_warning())
289
warning("trace2: could not connect to socket '%s' for '%s' tracing: %s",
290
path, tr2_sysenv_display_name(dst->sysenv_var),
293
tr2_dst_trace_disable(dst);
299
dst->initialized = 1;
305
static void tr2_dst_malformed_warning(struct tr2_dst *dst,
306
const char *tgt_value)
308
warning("trace2: unknown value for '%s': '%s'",
309
tr2_sysenv_display_name(dst->sysenv_var), tgt_value);
312
int tr2_dst_get_trace_fd(struct tr2_dst *dst)
314
const char *tgt_value;
317
if (dst->initialized)
320
dst->initialized = 1;
322
tgt_value = tr2_sysenv_get(dst->sysenv_var);
324
if (!tgt_value || !strcmp(tgt_value, "") || !strcmp(tgt_value, "0") ||
325
!strcasecmp(tgt_value, "false")) {
330
if (!strcmp(tgt_value, "1") || !strcasecmp(tgt_value, "true")) {
331
dst->fd = STDERR_FILENO;
335
if (strlen(tgt_value) == 1 && isdigit(*tgt_value)) {
336
dst->fd = atoi(tgt_value);
340
if (is_absolute_path(tgt_value)) {
341
if (is_directory(tgt_value))
342
return tr2_dst_try_auto_path(dst, tgt_value);
344
return tr2_dst_try_path(dst, tgt_value);
347
#ifndef NO_UNIX_SOCKETS
348
if (starts_with(tgt_value, PREFIX_AF_UNIX))
349
return tr2_dst_try_unix_domain_socket(dst, tgt_value);
353
tr2_dst_malformed_warning(dst, tgt_value);
354
tr2_dst_trace_disable(dst);
358
int tr2_dst_trace_want(struct tr2_dst *dst)
360
return !!tr2_dst_get_trace_fd(dst);
363
void tr2_dst_write_line(struct tr2_dst *dst, struct strbuf *buf_line)
365
int fd = tr2_dst_get_trace_fd(dst);
368
strbuf_complete_line(buf_line);
385
sigchain_push(SIGPIPE, SIG_IGN);
386
bytes = write(fd, buf_line->buf, buf_line->len);
387
sigchain_pop(SIGPIPE);
391
tr2_dst_trace_disable(dst);
392
if (tr2_dst_want_warning())
393
warning("unable to write trace to '%s': %s",
394
tr2_sysenv_display_name(dst->sysenv_var),