]> git.decadent.org.uk Git - videolink.git/blob - temp_file.cpp
Added auto-cleaning temporary directories.
[videolink.git] / temp_file.cpp
1 // Copyright 2005-6 Ben Hutchings <ben@decadent.org.uk>.
2 // See the file "COPYING" for licence details.
3
4 #include "temp_file.hpp"
5
6 #include <cassert>
7 #include <cerrno>
8 #include <cstdlib>
9 #include <cstring>
10 #include <iostream>
11 #include <ostream>
12 #include <stdexcept>
13 #include <vector>
14
15 #include <fcntl.h>
16 #include <unistd.h>
17
18 #include <glibmm/fileutils.h>
19 #include <glibmm/miscutils.h>
20
21 namespace
22 {
23     bool try_rmdir_recursive(const std::string & dir_name)
24     {
25         Glib::Dir dir(dir_name);
26         bool empty = true;
27
28         for (Glib::Dir::iterator it = dir.begin(), end = dir.end();
29              it != end;
30              ++it)
31         {
32             std::string full_name(Glib::build_filename(dir_name, *it));
33             if (!(Glib::file_test(full_name, Glib::FILE_TEST_IS_DIR)
34                   ? try_rmdir_recursive(full_name)
35                   : (unlink(full_name.c_str()) == 0 || errno == ENOENT)))
36                 empty = false;
37         }
38
39         return empty && (rmdir(dir_name.c_str()) == 0 || errno == ENOENT);
40     }
41
42     bool do_keep_all = false;
43 }
44
45 temp_file::temp_file(const std::string & base_name)
46 {
47     fd_ = Glib::file_open_tmp(name_, base_name);
48     assert(fd_ >= 0);
49
50     // Workaround bug in glibc <2.2 that may cause the file to be
51     // created with lax permissions.
52 #   ifdef __GLIBC__
53 #   if !__GLIBC_PREREQ(2, 2)
54     if (fchmod(fd_, S_IREAD|S_IWRITE) != 0 || ftruncate(fd_, 0) != 0)
55     {
56         close(fd_);
57         throw std::runtime_error(std::strerror(errno));
58     }
59 #   endif
60 #   endif
61 }
62
63 temp_file::~temp_file()
64 {
65     close();
66
67     if (!do_keep_all)
68     {
69         // Don't assert that this is successful.  The file could have
70         // been removed by another process.
71         unlink(name_.c_str());
72     }
73 }
74
75 void temp_file::close()
76 {
77     if (fd_ >= 0)
78     {
79         int result = ::close(fd_);
80         assert(result == 0);
81         fd_ = -1;
82     }
83 }
84
85 void temp_file::keep_all(bool keep)
86 {
87     do_keep_all = keep;
88 }
89
90 temp_dir::temp_dir(const std::string & base_name)
91 {
92     std::string tmp_dir_name(Glib::get_tmp_dir());
93     std::vector<char> template_name;
94     template_name.reserve(tmp_dir_name.size() + 1 + base_name.size() + 6 + 1);
95     name_.reserve(tmp_dir_name.size() + 1 + base_name.size() + 6);
96     template_name.assign(tmp_dir_name.begin(), tmp_dir_name.end());
97     template_name.insert(template_name.end(), '/');
98     template_name.insert(template_name.end(),
99                          base_name.begin(), base_name.end());
100     template_name.insert(template_name.end(), 6, 'X');
101     template_name.insert(template_name.end(), '\0');
102     if (mkdtemp(&template_name[0]))
103         name_.assign(template_name.begin(), template_name.end() - 1);
104     else
105         throw std::runtime_error(
106             std::string("mkdtemp: ").append(std::strerror(errno)));
107 }
108
109 temp_dir::~temp_dir()
110 {
111     if (!do_keep_all && !try_rmdir_recursive(name_))
112         std::cerr << "Warning: failed to remove temporary directory "
113                   << name_ << "\n";
114 }