logo

utils-std

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

mktemp.c (4087B)


  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 <errno.h>
  7. #include <limits.h> // PATH_MAX
  8. #include <stdbool.h>
  9. #include <stdio.h> // fprintf
  10. #include <stdlib.h> // getenv
  11. #include <string.h> // strerror
  12. #include <unistd.h> // getopt, getentropy
  13. #ifdef HAS_GETOPT_LONG
  14. #include <getopt.h>
  15. #endif
  16. const char *argv0 = "mktemp";
  17. static int
  18. unsafe_mktemp(char *template)
  19. {
  20. size_t l = strlen(template);
  21. size_t len = 0;
  22. size_t off = l - 1;
  23. while(off > 0 && template[off] == 'X')
  24. off--, len++;
  25. if(len < 6)
  26. {
  27. fprintf(stderr,
  28. "%s: error: Template '%s' is invalid (need >= 6 final X, got %zd)\n",
  29. argv0,
  30. template,
  31. len);
  32. return 1;
  33. }
  34. for(int retries = 100; 0 < retries; retries--)
  35. {
  36. unsigned long r = 0;
  37. if(getentropy(&r, sizeof(r)) != 0)
  38. {
  39. fprintf(stderr,
  40. "%s: error: Failed to get entropy for random filename: %s\n",
  41. argv0,
  42. strerror(errno));
  43. return 1;
  44. }
  45. for(int i = len; i > 0; i--, r >>= 5)
  46. template[off + i] = 'A' + (r & 15) + (r & 16) * 2;
  47. if(access(template, F_OK) != 0 && errno == ENOENT)
  48. {
  49. puts(template);
  50. return 0;
  51. }
  52. }
  53. fprintf(stderr, "%s: error: Exhausted all 100 attempts at generating a filename\n", argv0);
  54. return 1;
  55. }
  56. int
  57. main(int argc, char *argv[])
  58. {
  59. bool o_create_dir = false, o_quiet = false, o_unsafe = false;
  60. char template[PATH_MAX] = "tmp.XXXXXXXXXX";
  61. const char *tmpdir = NULL;
  62. int c = -1;
  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. while((c = getopt_long(argc, argv, "+:dqp:tu", opts, NULL)) != -1)
  76. #else
  77. while((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. }