THIS IS A TEST INSTANCE ONLY! REPOSITORIES CAN BE DELETED AT ANY TIME!

Git Source Code Mirror - This is a publish-only repository and all pull requests are ignored. Please follow Documentation/SubmittingPatches procedure for any of your improvements.
git
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

392 lines
9.8KB

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