trace2 / tr2_dst.con commit Merge branch 'nd/diff-parseopt-4' (dcd6a8c)
   1#include "cache.h"
   2#include "trace2/tr2_dst.h"
   3
   4/*
   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.
  10 *
  11 * Enable the following environment variable to see these warnings.
  12 */
  13#define TR2_ENVVAR_DST_DEBUG "GIT_TR2_DST_DEBUG"
  14
  15static int tr2_dst_want_warning(void)
  16{
  17        static int tr2env_dst_debug = -1;
  18
  19        if (tr2env_dst_debug == -1) {
  20                const char *env_value = getenv(TR2_ENVVAR_DST_DEBUG);
  21                if (!env_value || !*env_value)
  22                        tr2env_dst_debug = 0;
  23                else
  24                        tr2env_dst_debug = atoi(env_value) > 0;
  25        }
  26
  27        return tr2env_dst_debug;
  28}
  29
  30void tr2_dst_trace_disable(struct tr2_dst *dst)
  31{
  32        if (dst->need_close)
  33                close(dst->fd);
  34        dst->fd = 0;
  35        dst->initialized = 1;
  36        dst->need_close = 0;
  37}
  38
  39static int tr2_dst_try_path(struct tr2_dst *dst, const char *tgt_value)
  40{
  41        int fd = open(tgt_value, O_WRONLY | O_APPEND | O_CREAT, 0666);
  42        if (fd == -1) {
  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));
  46
  47                tr2_dst_trace_disable(dst);
  48                return 0;
  49        }
  50
  51        dst->fd = fd;
  52        dst->need_close = 1;
  53        dst->initialized = 1;
  54
  55        return dst->fd;
  56}
  57
  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:"
  62
  63static int tr2_dst_try_uds_connect(const char *path, int sock_type, int *out_fd)
  64{
  65        int fd;
  66        struct sockaddr_un sa;
  67
  68        fd = socket(AF_UNIX, sock_type, 0);
  69        if (fd == -1)
  70                return errno;
  71
  72        sa.sun_family = AF_UNIX;
  73        strlcpy(sa.sun_path, path, sizeof(sa.sun_path));
  74
  75        if (connect(fd, (struct sockaddr *)&sa, sizeof(sa)) == -1) {
  76                int e = errno;
  77                close(fd);
  78                return e;
  79        }
  80
  81        *out_fd = fd;
  82        return 0;
  83}
  84
  85#define TR2_DST_UDS_TRY_STREAM (1 << 0)
  86#define TR2_DST_UDS_TRY_DGRAM  (1 << 1)
  87
  88static int tr2_dst_try_unix_domain_socket(struct tr2_dst *dst,
  89                                          const char *tgt_value)
  90{
  91        unsigned int uds_try = 0;
  92        int fd;
  93        int e;
  94        const char *path = NULL;
  95
  96        /*
  97         * Allow "af_unix:[<type>:]<absolute_path>"
  98         *
  99         * Trace2 always writes complete individual messages (without
 100         * chunking), so we can talk to either DGRAM or STREAM type sockets.
 101         *
 102         * Allow the user to explicitly request the socket type.
 103         *
 104         * If they omit the socket type, try one and then the other.
 105         */
 106
 107        if (skip_prefix(tgt_value, PREFIX_AF_UNIX_STREAM, &path))
 108                uds_try |= TR2_DST_UDS_TRY_STREAM;
 109
 110        else if (skip_prefix(tgt_value, PREFIX_AF_UNIX_DGRAM, &path))
 111                uds_try |= TR2_DST_UDS_TRY_DGRAM;
 112
 113        else if (skip_prefix(tgt_value, PREFIX_AF_UNIX, &path))
 114                uds_try |= TR2_DST_UDS_TRY_STREAM | TR2_DST_UDS_TRY_DGRAM;
 115
 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);
 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",
 129                                path, dst->env_var_name);
 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",
 151                        path, dst->env_var_name, strerror(e));
 152
 153        tr2_dst_trace_disable(dst);
 154        return 0;
 155
 156connected:
 157        dst->fd = fd;
 158        dst->need_close = 1;
 159        dst->initialized = 1;
 160
 161        return dst->fd;
 162}
 163#endif
 164
 165static void tr2_dst_malformed_warning(struct tr2_dst *dst,
 166                                      const char *tgt_value)
 167{
 168        struct strbuf buf = STRBUF_INIT;
 169
 170        strbuf_addf(&buf, "trace2: unknown value for '%s': '%s'",
 171                    dst->env_var_name, tgt_value);
 172        warning("%s", buf.buf);
 173
 174        strbuf_release(&buf);
 175}
 176
 177int tr2_dst_get_trace_fd(struct tr2_dst *dst)
 178{
 179        const char *tgt_value;
 180
 181        /* don't open twice */
 182        if (dst->initialized)
 183                return dst->fd;
 184
 185        dst->initialized = 1;
 186
 187        tgt_value = getenv(dst->env_var_name);
 188
 189        if (!tgt_value || !strcmp(tgt_value, "") || !strcmp(tgt_value, "0") ||
 190            !strcasecmp(tgt_value, "false")) {
 191                dst->fd = 0;
 192                return dst->fd;
 193        }
 194
 195        if (!strcmp(tgt_value, "1") || !strcasecmp(tgt_value, "true")) {
 196                dst->fd = STDERR_FILENO;
 197                return dst->fd;
 198        }
 199
 200        if (strlen(tgt_value) == 1 && isdigit(*tgt_value)) {
 201                dst->fd = atoi(tgt_value);
 202                return dst->fd;
 203        }
 204
 205        if (is_absolute_path(tgt_value))
 206                return tr2_dst_try_path(dst, tgt_value);
 207
 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);
 211#endif
 212
 213        /* Always warn about malformed values. */
 214        tr2_dst_malformed_warning(dst, tgt_value);
 215        tr2_dst_trace_disable(dst);
 216        return 0;
 217}
 218
 219int tr2_dst_trace_want(struct tr2_dst *dst)
 220{
 221        return !!tr2_dst_get_trace_fd(dst);
 222}
 223
 224void tr2_dst_write_line(struct tr2_dst *dst, struct strbuf *buf_line)
 225{
 226        int fd = tr2_dst_get_trace_fd(dst);
 227
 228        strbuf_complete_line(buf_line); /* ensure final NL on buffer */
 229
 230        /*
 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.
 238         *
 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
 241         * a short-write.
 242         *
 243         * If we get an IO error, just close the trace dst.
 244         */
 245        if (write(fd, buf_line->buf, buf_line->len) >= 0)
 246                return;
 247
 248        if (tr2_dst_want_warning())
 249                warning("unable to write trace to '%s': %s", dst->env_var_name,
 250                        strerror(errno));
 251        tr2_dst_trace_disable(dst);
 252}