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