BRL-CAD
temp.c
Go to the documentation of this file.
1 /* T E M P . C
2  * BRL-CAD
3  *
4  * Copyright (c) 2001-2014 United States Government as represented by
5  * the U.S. Army Research Laboratory.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public License
9  * version 2.1 as published by the Free Software Foundation.
10  *
11  * This library is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this file; see the file named COPYING for more
18  * information.
19  */
20 
21 #include "common.h"
22 
23 #include <stdlib.h>
24 #include <string.h>
25 #include <time.h>
26 #ifdef HAVE_SYS_TYPES_H
27 # include <sys/types.h>
28 #endif
29 #ifdef HAVE_SYS_STAT_H
30 # include <sys/stat.h>
31 #endif
32 #include "bio.h"
33 
34 #include "bu/file.h"
35 #include "bu/log.h"
36 #include "bu/list.h"
37 #include "bu/malloc.h"
38 #include "bu/vls.h"
39 
40 #define _TF_FAIL "WARNING: Unable to create a temporary file\n"
41 
42 
43 /* c99 doesn't declare these */
44 #if !defined(_WIN32) || defined(__CYGWIN__)
45 # if !defined(__cplusplus)
46 extern FILE *fdopen(int, const char *);
47 # endif
48 #endif
49 
50 
52  struct bu_list l;
53  struct bu_vls fn;
54  int fd;
55 };
56 
57 static int temp_files = 0;
58 static struct temp_file_list *TF = NULL;
59 
60 
61 HIDDEN void
63 {
64  struct temp_file_list *popped;
65  if (!TF) {
66  return;
67  }
68 
69  /* close all files, free their nodes, and unlink */
70  while (BU_LIST_WHILE(popped, temp_file_list, &(TF->l))) {
71  if (!popped)
72  break;
73 
74  /* take it off the list */
75  BU_LIST_DEQUEUE(&(popped->l));
76 
77  /* close up shop */
78  if (popped->fd != -1) {
79  close(popped->fd);
80  popped->fd = -1;
81  }
82 
83  /* burn the house down */
84  if (BU_VLS_IS_INITIALIZED(&popped->fn) && bu_vls_addr(&popped->fn)) {
85  bu_file_delete(bu_vls_addr(&popped->fn));
86  bu_vls_free(&popped->fn);
87  }
88  BU_PUT(popped, struct temp_file_list);
89  }
90 
91  /* free the head */
92  if (TF->fd != -1) {
93  close(TF->fd);
94  TF->fd = -1;
95  }
96  if (BU_VLS_IS_INITIALIZED(&TF->fn) && bu_vls_addr(&TF->fn)) {
98  bu_vls_free(&TF->fn);
99  }
100  BU_PUT(TF, struct temp_file_list);
101 }
102 
103 
104 HIDDEN void
105 temp_add_to_list(const char *fn, int fd)
106 {
107  struct temp_file_list *newtf;
108 
109  temp_files++;
110 
111  if (temp_files == 1) {
112  /* schedule files for closure on exit */
113  atexit(temp_close_files);
114 
115  BU_GET(TF, struct temp_file_list);
116  BU_LIST_INIT(&(TF->l));
117  bu_vls_init(&TF->fn);
118 
119  bu_vls_strcpy(&TF->fn, fn);
120  TF->fd = fd;
121 
122  return;
123  }
124 
125  BU_GET(newtf, struct temp_file_list);
126  BU_LIST_INIT(&(TF->l));
127  bu_vls_init(&TF->fn);
128 
129  bu_vls_strcpy(&TF->fn, fn);
130  newtf->fd = fd;
131 
132  BU_LIST_PUSH(&(TF->l), &(newtf->l));
133 
134  return;
135 }
136 
137 
138 #ifndef HAVE_MKSTEMP
139 HIDDEN int
140 mkstemp(char *file_template)
141 {
142  int fd = -1;
143  int counter = 0;
144  size_t i;
145  size_t start, end;
146 
147  static const char replace[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
148  static int replacelen = sizeof(replace) - 1;
149 
150  if (!file_template || file_template[0] == '\0')
151  return -1;
152 
153  /* identify the replacement suffix */
154  start = end = strlen(file_template)-1;
155  for (i=strlen(file_template)-1; i>=0; i--) {
156  if (file_template[i] != 'X') {
157  break;
158  }
159  end = i;
160  }
161 
162  do {
163  /* replace the template with random chars */
164  srand((unsigned)time(NULL));
165  for (i=start; i>=end; i--) {
166  file_template[i] = replace[(int)(replacelen * ((double)rand() / (double)RAND_MAX))];
167  }
168  fd = open(file_template, O_CREAT | O_EXCL | O_TRUNC | O_RDWR | O_TEMPORARY, S_IRUSR | S_IWUSR);
169  } while ((fd == -1) && (counter++ < 1000));
170 
171  return fd;
172 }
173 #else
174 /* for c99 strict, doesn't declare */
175 extern int mkstemp(char *);
176 #endif
177 
178 
179 FILE *
180 bu_temp_file(char *filepath, size_t len)
181 {
182  FILE *fp = NULL;
183  int i;
184  int fd = -1;
185  char tempfile[MAXPATHLEN];
186  mode_t mask;
187  const char *dir = NULL;
188  const char *envdirs[] = {"TMPDIR", "TEMP", "TMP", NULL};
189  const char *trydirs[] = {
190 #ifdef _WIN32
191  "C:\\TEMP",
192  "C:\\WINDOWS\\TEMP",
193 #endif
194  "/tmp",
195  "/usr/tmp",
196  "/var/tmp",
197  ".", /* last resort */
198  NULL
199  };
200 
201  if (len > MAXPATHLEN) {
202  len = MAXPATHLEN;
203  }
204 
205  /* check environment variable directories */
206  for (i=0; envdirs[i]; i++) {
207  dir = getenv(envdirs[i]);
208  if (dir && dir[0] != '\0' && bu_file_writable(dir) && bu_file_executable(dir)) {
209  break;
210  }
211  }
212 
213  if (!dir) {
214  /* try various directories */
215  for (i=0; trydirs[i]; i++) {
216  dir = trydirs[i];
217  if (dir && dir[0] != '\0' && bu_file_writable(dir) && bu_file_executable(dir)) {
218  break;
219  }
220  }
221  }
222 
223  if (UNLIKELY(!dir)) {
224  /* give up */
225  bu_log("Unable to find a suitable temp directory\n");
226  bu_log(_TF_FAIL);
227  return NULL;
228  }
229 
230  snprintf(tempfile, MAXPATHLEN, "%s%cBRL-CAD_temp_XXXXXXX", dir, BU_DIR_SEPARATOR);
231 
232  /* secure the temp file with user read-write only */
233  mask = umask(S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH);
234  fd = mkstemp(tempfile);
235  (void)umask(mask); /* restore */
236 
237  if (UNLIKELY(fd == -1)) {
238  perror("mkstemp");
239  bu_log(_TF_FAIL);
240  return NULL;
241  }
242 
243  if (filepath) {
244  if (UNLIKELY(len < strlen(tempfile))) {
245  bu_log("INTERNAL ERROR: bu_temp_file filepath buffer size is insufficient (%zu < %zu)\n", len, strlen(tempfile));
246  }
247  snprintf(filepath, len, "%s", tempfile);
248  }
249 
250  fp = fdopen(fd, "wb+");
251  if (UNLIKELY(fp == NULL)) {
252  perror("fdopen");
253  bu_log(_TF_FAIL);
254  close(fd);
255  return NULL;
256  }
257 
258  /* add the file to the atexit auto-close list */
259  temp_add_to_list(tempfile, fd);
260 
261  return fp;
262 }
263 
264 /*
265  * Local Variables:
266  * mode: C
267  * tab-width: 8
268  * indent-tabs-mode: t
269  * c-file-style: "stroustrup"
270  * End:
271  * ex: shiftwidth=4 tabstop=8
272  */
void bu_vls_init(struct bu_vls *vp)
Definition: vls.c:56
void bu_log(const char *,...) _BU_ATTR_PRINTF12
Definition: log.c:176
Definition: list.h:118
#define BU_VLS_IS_INITIALIZED(_vp)
Definition: vls.h:92
Header file for the BRL-CAD common definitions.
long time(time_t *)
#define HIDDEN
Definition: common.h:86
int bu_file_writable(const char *path)
Definition: file.c:230
void bu_vls_free(struct bu_vls *vp)
Definition: vls.c:248
FILE * fdopen(int, const char *)
FILE * bu_temp_file(char *filepath, size_t len)
Definition: temp.c:180
#define BU_GET(_ptr, _type)
Definition: malloc.h:201
#define BU_PUT(_ptr, _type)
Definition: malloc.h:215
char * bu_vls_addr(const struct bu_vls *vp)
Definition: vls.c:111
#define BU_LIST_WHILE(p, structure, hp)
Definition: list.h:410
#define BU_DIR_SEPARATOR
Definition: defines.h:97
#define BU_LIST_PUSH(hp, p)
Definition: list.h:246
HIDDEN void temp_add_to_list(const char *fn, int fd)
Definition: temp.c:105
#define BU_LIST_INIT(_hp)
Definition: list.h:148
HIDDEN void temp_close_files(void)
Definition: temp.c:62
int fd
Definition: temp.c:54
HIDDEN int mkstemp(char *file_template)
Definition: temp.c:140
void bu_vls_strcpy(struct bu_vls *vp, const char *s)
Definition: vls.c:310
#define BU_LIST_DEQUEUE(cur)
Definition: list.h:209
struct bu_list l
Definition: temp.c:52
int bu_file_executable(const char *path)
Definition: file.c:237
Definition: vls.h:56
struct bu_vls fn
Definition: temp.c:53
#define _TF_FAIL
Definition: temp.c:40
int bu_file_delete(const char *path)
Definition: file.c:278
#define UNLIKELY(expression)
Definition: common.h:282
#define MAXPATHLEN
Definition: defines.h:113