Fix Joey's report of rename failing across moint points.
[ccan] / tools / tools.c
1 #include <ccan/talloc/talloc.h>
2 #include <ccan/grab_file/grab_file.h>
3 #include <ccan/noerr/noerr.h>
4 #include <ccan/read_write_all/read_write_all.h>
5 #include <sys/stat.h>
6 #include <sys/types.h>
7 #include <fcntl.h>
8 #include <string.h>
9 #include <unistd.h>
10 #include <stdarg.h>
11 #include <errno.h>
12 #include <err.h>
13 #include "tools.h"
14
15 static char *tmpdir = NULL;
16 static unsigned int count;
17
18 char *talloc_basename(const void *ctx, const char *dir)
19 {
20         char *p = strrchr(dir, '/');
21
22         if (!p)
23                 return (char *)dir;
24         return talloc_strdup(ctx, p+1);
25 }
26
27 char *talloc_dirname(const void *ctx, const char *dir)
28 {
29         char *p = strrchr(dir, '/');
30
31         if (!p)
32                 return talloc_strdup(ctx, ".");
33         return talloc_strndup(ctx, dir, p - dir);
34 }
35
36 char *talloc_getcwd(const void *ctx)
37 {
38         unsigned int len;
39         char *cwd;
40
41         /* *This* is why people hate C. */
42         len = 32;
43         cwd = talloc_array(ctx, char, len);
44         while (!getcwd(cwd, len)) {
45                 if (errno != ERANGE) {
46                         talloc_free(cwd);
47                         return NULL;
48                 }
49                 cwd = talloc_realloc(ctx, cwd, char, len *= 2);
50         }
51         return cwd;
52 }
53
54 /* Returns output if command fails. */
55 char *run_command(const void *ctx, const char *fmt, ...)
56 {
57         va_list ap;
58         char *cmd, *contents;
59         FILE *pipe;
60
61         va_start(ap, fmt);
62         cmd = talloc_vasprintf(ctx, fmt, ap);
63         va_end(ap);
64
65         /* Ensure stderr gets to us too. */
66         cmd = talloc_asprintf_append(cmd, " 2>&1");
67
68         pipe = popen(cmd, "r");
69         if (!pipe)
70                 return talloc_asprintf(ctx, "Failed to run '%s'", cmd);
71
72         contents = grab_fd(cmd, fileno(pipe), NULL);
73         if (pclose(pipe) != 0)
74                 return talloc_asprintf(ctx, "Running '%s':\n%s",
75                                        cmd, contents);
76
77         talloc_free(cmd);
78         return NULL;
79 }
80
81 static int unlink_all(char *dir)
82 {
83         char cmd[strlen(dir) + sizeof("rm -rf ")];
84         sprintf(cmd, "rm -rf %s", dir);
85         if (system(cmd) != 0)
86                 warn("Could not remove temporary work in %s", dir);
87         return 0;
88 }
89
90 char *temp_file(const void *ctx, const char *extension)
91 {
92         /* For first call, create dir. */
93         while (!tmpdir) {
94                 tmpdir = getenv("TMPDIR");
95                 if (!tmpdir)
96                         tmpdir = "/tmp";
97                 tmpdir = talloc_asprintf(talloc_autofree_context(),
98                                          "%s/ccanlint-%u.%lu",
99                                          tmpdir, getpid(), random());
100                 if (mkdir(tmpdir, 0700) != 0) {
101                         if (errno == EEXIST) {
102                                 talloc_free(tmpdir);
103                                 tmpdir = NULL;
104                                 continue;
105                         }
106                         err(1, "mkdir %s failed", tmpdir);
107                 }
108                 talloc_set_destructor(tmpdir, unlink_all);
109         }
110
111         return talloc_asprintf(ctx, "%s/%u%s", tmpdir, count++, extension);
112 }
113
114 bool move_file(const char *oldname, const char *newname)
115 {
116         char *contents;
117         size_t size;
118         int fd;
119         bool ret;
120
121         /* Simple case: rename works. */
122         if (rename(oldname, newname) == 0)
123                 return true;
124
125         /* Try copy and delete: not atomic! */
126         contents = grab_file(NULL, oldname, &size);
127         if (!contents)
128                 return false;
129
130         fd = open(newname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
131         if (fd < 0) {
132                 ret = false;
133                 goto free;
134         }
135
136         ret = write_all(fd, contents, size);
137         if (close(fd) != 0)
138                 ret = false;
139
140         if (ret)
141                 unlink(oldname);
142         else
143                 unlink(newname);
144
145 free:
146         talloc_free(contents);
147         return ret;
148 }