trace2 / tr2_dst.con commit Merge branch 'vk/autoconf-gettext' (f832bcc)
   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
  21static 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
  36void 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
  45static 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
  94static 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
 118static 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
 143static 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
 203error:
 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
 211connected:
 212        dst->fd = fd;
 213        dst->need_close = 1;
 214        dst->initialized = 1;
 215
 216        return dst->fd;
 217}
 218#endif
 219
 220static 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
 232int 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
 278int tr2_dst_trace_want(struct tr2_dst *dst)
 279{
 280        return !!tr2_dst_get_trace_fd(dst);
 281}
 282
 283void 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}