logo

utils-std

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

mktemp.c (3556B)


  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. const char *argv0 = "mktemp";
  14. static int
  15. unsafe_mktemp(char *template)
  16. {
  17. size_t l = strlen(template);
  18. size_t len = 0;
  19. size_t off = l - 1;
  20. while(off > 0 && template[off] == 'X')
  21. off--, len++;
  22. if(len < 6)
  23. {
  24. fprintf(stderr,
  25. "%s: error: Template '%s' is invalid (need >= 6 final X, got %zd)\n",
  26. argv0,
  27. template,
  28. len);
  29. return 1;
  30. }
  31. for(int retries = 100; 0 < retries; retries--)
  32. {
  33. unsigned long r = 0;
  34. if(getentropy(&r, sizeof(r)) != 0)
  35. {
  36. fprintf(stderr,
  37. "%s: error: Failed to get entropy for random filename: %s\n",
  38. argv0,
  39. strerror(errno));
  40. return 1;
  41. }
  42. for(int i = len; i > 0; i--, r >>= 5)
  43. template[off + i] = 'A' + (r & 15) + (r & 16) * 2;
  44. if(access(template, F_OK) != 0 && errno == ENOENT)
  45. {
  46. puts(template);
  47. return 0;
  48. }
  49. }
  50. fprintf(stderr, "%s: error: Exhausted all 100 attempts at generating a filename\n", argv0);
  51. return 1;
  52. }
  53. int
  54. main(int argc, char *argv[])
  55. {
  56. bool o_create_dir = false, o_quiet = false, o_unsafe = false;
  57. char template[PATH_MAX] = "tmp.XXXXXXXXXX";
  58. const char *tmpdir = NULL;
  59. int c = -1;
  60. while((c = getopt(argc, argv, ":dqp:tu")) != -1)
  61. {
  62. switch(c)
  63. {
  64. case 'd':
  65. o_create_dir = true;
  66. break;
  67. case 'q':
  68. o_quiet = true;
  69. break;
  70. case 'p':
  71. tmpdir = optarg;
  72. break;
  73. case 't':
  74. if(tmpdir == NULL) tmpdir = getenv("TMPDIR");
  75. if(tmpdir == NULL) tmpdir = "/tmp";
  76. break;
  77. case 'u':
  78. o_unsafe = true;
  79. break;
  80. case '?':
  81. fprintf(stderr, "%s: error: Unrecognised option: '-%c'\n", argv0, optopt);
  82. return 1;
  83. default:
  84. fprintf(stderr, "%s: error: Unhandled getopt case '%c'\n", argv0, c);
  85. abort();
  86. }
  87. }
  88. argc -= optind;
  89. argv += optind;
  90. if(argc == 1)
  91. {
  92. strncpy(template, *argv, PATH_MAX);
  93. }
  94. else if(argc > 1)
  95. {
  96. fprintf(stderr, "%s: error: Only one template argument is supported, got %d\n", argv0, argc);
  97. return 1;
  98. }
  99. if(tmpdir)
  100. {
  101. if(chdir(tmpdir) < 0)
  102. {
  103. fprintf(stderr,
  104. "%s: error: Failed changing directory into tmpdir '%s': %s\n",
  105. argv0,
  106. tmpdir,
  107. strerror(errno));
  108. return 1;
  109. }
  110. printf("%s/", tmpdir);
  111. }
  112. char template_copy[PATH_MAX] = "";
  113. memcpy(template_copy, template, PATH_MAX);
  114. if(o_unsafe) return unsafe_mktemp(template);
  115. if(o_create_dir)
  116. {
  117. char *dir = mkdtemp(template);
  118. if(dir == NULL)
  119. {
  120. if(!o_quiet)
  121. fprintf(stderr,
  122. "%s: error: Failed creating random directory with template '%s': %s\n",
  123. argv0,
  124. template_copy,
  125. strerror(errno));
  126. return 1;
  127. }
  128. puts(dir);
  129. return 0;
  130. }
  131. int fd = mkstemp(template);
  132. if(fd < 0)
  133. {
  134. if(!o_quiet)
  135. fprintf(stderr,
  136. "%s: error: Failed creating random file with template '%s': %s\n",
  137. argv0,
  138. template_copy,
  139. strerror(errno));
  140. return 1;
  141. }
  142. puts(template);
  143. if(close(fd) < 0)
  144. {
  145. if(!o_quiet) fprintf(stderr, "%s: error: Failed closing file: %s\n", argv0, strerror(errno));
  146. return 1;
  147. }
  148. return 0;
  149. }