1 // -*- coding: utf-8 -*- 2 // Copyright (C) 2013, 2015, 2017-2019 Laboratoire de Recherche et 3 // Développement de l'Epita (LRDE). 4 // 5 // This file is part of Spot, a model checking library. 6 // 7 // Spot is free software; you can redistribute it and/or modify it 8 // under the terms of the GNU General Public License as published by 9 // the Free Software Foundation; either version 3 of the License, or 10 // (at your option) any later version. 11 // 12 // Spot is distributed in the hope that it will be useful, but WITHOUT 13 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 14 // or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public 15 // License for more details. 16 // 17 // You should have received a copy of the GNU General Public License 18 // along with this program. If not, see <http://www.gnu.org/licenses/>. 19 20 #include "config.h" 21 #include <spot/misc/tmpfile.hh> 22 #include <errno.h> 23 #include <stdlib.h> 24 #include <unistd.h> 25 #include <string.h> 26 27 using namespace std::string_literals; 28 29 namespace spot 30 { 31 namespace 32 { 33 std::list<temporary_file*> to_clean; 34 35 static const char* get_tmpdir()36 get_tmpdir() 37 { 38 const char* res = secure_getenv("SPOT_TMPDIR"); 39 if (res) 40 return res; 41 return secure_getenv("TMPDIR"); 42 } 43 44 static int create_temporary_file(const char * prefix,const char * suffix,char ** name)45 create_temporary_file(const char* prefix, 46 const char* suffix, 47 char** name) 48 { 49 static const char* tmpdir = get_tmpdir(); 50 static int tmpdirlen = tmpdir ? strlen(tmpdir) : 0; 51 52 size_t len = strlen(prefix); 53 size_t slen = 0; 54 if (suffix) 55 len += slen = strlen(suffix); 56 char* x = *name = static_cast<char*>(malloc(tmpdirlen + 1 + len + 6 + 1)); 57 if (!x) 58 throw std::bad_alloc(); 59 if (tmpdir) 60 { 61 x = stpcpy(x, tmpdir); 62 if (x[-1] != '/') 63 *x++ = '/'; 64 } 65 x = stpcpy(x, prefix); 66 x = stpcpy(x, "XXXXXX"); 67 int fd; 68 if (suffix) 69 { 70 stpcpy(x, suffix); 71 fd = mkstemps(*name, slen); 72 } 73 else 74 { 75 fd = mkstemp(*name); 76 } 77 if (fd < 0) 78 { 79 std::string err = ("failed to create temporary file "s + *name 80 + ": " + strerror(errno)); 81 if (errno == EACCES) 82 { 83 if (tmpdir) 84 err += ("\nConsider setting the SPOT_TMPDIR environment " 85 "variable to a writable directory."); 86 else 87 err += ("\nConsider executing this from a writable " 88 "directory, or setting\nthe SPOT_TMPDIR environment " 89 "variable to such a directory."); 90 } 91 else if (tmpdir) 92 { 93 const char* dir = 94 secure_getenv("SPOT_TMPDIR") ? "SPOT_TMPDIR" : "TMPDIR"; 95 err += ("\nNote that the directory comes from the "s 96 + dir 97 + " environment variable."); 98 } 99 throw std::runtime_error(err); 100 } 101 return fd; 102 } 103 } 104 105 temporary_file(char * name,cleanpos_t cp)106 temporary_file::temporary_file(char* name, cleanpos_t cp) 107 : name_(name), cleanpos_(cp) 108 { 109 } 110 ~temporary_file()111 temporary_file::~temporary_file() 112 { 113 static bool must_unlink = !secure_getenv("SPOT_TMPKEEP"); 114 if (must_unlink) 115 unlink(name_); 116 free(name_); 117 to_clean.erase(cleanpos_); 118 } 119 open_temporary_file(char * name,cleanpos_t cp,int fd)120 open_temporary_file::open_temporary_file(char* name, cleanpos_t cp, int fd) 121 : temporary_file(name, cp), fd_(fd) 122 { 123 } 124 ~open_temporary_file()125 open_temporary_file::~open_temporary_file() 126 { 127 close(); 128 } 129 130 void close()131 open_temporary_file::close() 132 { 133 if (fd_ < 0) 134 return; 135 if (::close(fd_)) 136 throw std::runtime_error("failed to close "s + name_); 137 fd_ = -1; 138 } 139 140 temporary_file* create_tmpfile(const char * prefix,const char * suffix)141 create_tmpfile(const char* prefix, const char* suffix) 142 { 143 char* name; 144 int fd = create_temporary_file(prefix, suffix, &name); 145 if (close(fd)) 146 throw std::runtime_error("failed to close "s + name); 147 auto cp = to_clean.insert(to_clean.end(), nullptr); 148 *cp = new temporary_file(name, cp); 149 return *cp; 150 } 151 152 open_temporary_file* create_open_tmpfile(const char * prefix,const char * suffix)153 create_open_tmpfile(const char* prefix, const char* suffix) 154 { 155 char* name; 156 int fd = create_temporary_file(prefix, suffix, &name); 157 auto cp = to_clean.insert(to_clean.end(), nullptr); 158 open_temporary_file* otf = new open_temporary_file(name, cp, fd); 159 *cp = otf; 160 return otf; 161 } 162 163 void cleanup_tmpfiles()164 cleanup_tmpfiles() 165 { 166 while (!to_clean.empty()) 167 delete to_clean.front(); 168 } 169 } 170