logo

utils-std

Collection of commonly available Unix tools git clone https://anongit.hacktivis.me/git/utils-std.git/

mktemp.c (4129B)


  1. // utils-std: Collection of commonly available Unix tools
  2. // SPDX-FileCopyrightText: 2017 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me>
  3. // SPDX-License-Identifier: MPL-2.0
  4. // getentropy got in POSIX.1-2024 but defining _POSIX_C_SOURCE causes too much side-effects
  5. #define _DEFAULT_SOURCE // getentropy
  6. #include "../config.h" // HAS_*
  7. #include <errno.h>
  8. #include <limits.h> // PATH_MAX
  9. #include <stdbool.h>
  10. #include <stdio.h> // fprintf
  11. #include <stdlib.h> // getenv
  12. #include <string.h> // strerror
  13. #include <unistd.h> // getopt, getentropy
  14. #ifdef HAS_GETOPT_LONG
  15. #include <getopt.h>
  16. #endif
  17. const char *argv0 = "mktemp";
  18. static int
  19. unsafe_mktemp(char *template)
  20. {
  21. size_t l = strlen(template);
  22. size_t len = 0;
  23. size_t off = l - 1;
  24. while(off > 0 && template[off] == 'X')
  25. off--, len++;
  26. if(len < 6)
  27. {
  28. fprintf(stderr,
  29. "%s: error: Template '%s' is invalid (need >= 6 final X, got %zd)\n",
  30. argv0,
  31. template,
  32. len);
  33. return 1;
  34. }
  35. for(int retries = 100; 0 < retries; retries--)
  36. {
  37. unsigned long r = 0;
  38. if(getentropy(&r, sizeof(r)) != 0)
  39. {
  40. fprintf(stderr,
  41. "%s: error: Failed to get entropy for random filename: %s\n",
  42. argv0,
  43. strerror(errno));
  44. return 1;
  45. }
  46. for(int i = len; i > 0; i--, r >>= 5)
  47. template[off + i] = 'A' + (r & 15) + (r & 16) * 2;
  48. if(access(template, F_OK) != 0 && errno == ENOENT)
  49. {
  50. puts(template);
  51. return 0;
  52. }
  53. }
  54. fprintf(stderr, "%s: error: Exhausted all 100 attempts at generating a filename\n", argv0);
  55. return 1;
  56. }
  57. int
  58. main(int argc, char *argv[])
  59. {
  60. bool o_create_dir = false, o_quiet = false, o_unsafe = false;
  61. char template[PATH_MAX] = "tmp.XXXXXXXXXX";
  62. const char *tmpdir = NULL;
  63. #ifdef HAS_GETOPT_LONG
  64. // Strictly for GNUisms compatibility so no long-only options
  65. // clang-format off
  66. static struct option opts[] = {
  67. {"directory", no_argument, 0, 'd'},
  68. {"dry-run", required_argument, 0, 'u'},
  69. {"quiet", no_argument, 0, 'q'},
  70. {"tmpdir", required_argument, 0, 'p'},
  71. {0, 0, 0, 0},
  72. };
  73. // clang-format on
  74. // Need + as first character to get POSIX-style option parsing
  75. for(int c = -1; (c = getopt_long(argc, argv, "+:dqp:tu", opts, NULL)) != -1;)
  76. #else
  77. for(int c = -1; (c = getopt(argc, argv, ":dqp:tu")) != -1;)
  78. #endif
  79. {
  80. switch(c)
  81. {
  82. case 'd':
  83. o_create_dir = true;
  84. break;
  85. case 'q':
  86. o_quiet = true;
  87. break;
  88. case 'p':
  89. tmpdir = optarg;
  90. break;
  91. case 't':
  92. if(tmpdir == NULL) tmpdir = getenv("TMPDIR");
  93. if(tmpdir == NULL) tmpdir = "/tmp";
  94. break;
  95. case 'u':
  96. o_unsafe = true;
  97. break;
  98. case '?':
  99. fprintf(stderr, "%s: error: Unrecognised option: '-%c'\n", argv0, optopt);
  100. return 1;
  101. default:
  102. fprintf(stderr, "%s: error: Unhandled getopt case '%c'\n", argv0, c);
  103. abort();
  104. }
  105. }
  106. argc -= optind;
  107. argv += optind;
  108. if(argc == 1)
  109. {
  110. strncpy(template, *argv, PATH_MAX);
  111. }
  112. else if(argc > 1)
  113. {
  114. fprintf(stderr, "%s: error: Only one template argument is supported, got %d\n", argv0, argc);
  115. return 1;
  116. }
  117. if(tmpdir)
  118. {
  119. if(chdir(tmpdir) < 0)
  120. {
  121. fprintf(stderr,
  122. "%s: error: Failed changing directory into tmpdir '%s': %s\n",
  123. argv0,
  124. tmpdir,
  125. strerror(errno));
  126. return 1;
  127. }
  128. printf("%s/", tmpdir);
  129. }
  130. char template_copy[PATH_MAX] = "";
  131. memcpy(template_copy, template, PATH_MAX);
  132. if(o_unsafe) return unsafe_mktemp(template);
  133. if(o_create_dir)
  134. {
  135. char *dir = mkdtemp(template);
  136. if(dir == NULL)
  137. {
  138. if(!o_quiet)
  139. fprintf(stderr,
  140. "%s: error: Failed creating random directory with template '%s': %s\n",
  141. argv0,
  142. template_copy,
  143. strerror(errno));
  144. return 1;
  145. }
  146. puts(dir);
  147. return 0;
  148. }
  149. int fd = mkstemp(template);
  150. if(fd < 0)
  151. {
  152. if(!o_quiet)
  153. fprintf(stderr,
  154. "%s: error: Failed creating random file with template '%s': %s\n",
  155. argv0,
  156. template_copy,
  157. strerror(errno));
  158. return 1;
  159. }
  160. puts(template);
  161. if(close(fd) < 0)
  162. {
  163. if(!o_quiet) fprintf(stderr, "%s: error: Failed closing file: %s\n", argv0, strerror(errno));
  164. return 1;
  165. }
  166. return 0;
  167. }