tools: use tal instead of talloc.
[ccan] / tools / tools.c
1 #include <ccan/take/take.h>
2 #include <ccan/err/err.h>
3 #include <ccan/noerr/noerr.h>
4 #include <ccan/rbuf/rbuf.h>
5 #include <ccan/read_write_all/read_write_all.h>
6 #include <ccan/noerr/noerr.h>
7 #include <ccan/time/time.h>
8 #include <sys/stat.h>
9 #include <sys/types.h>
10 #include <sys/time.h>
11 #include <sys/wait.h>
12 #include <fcntl.h>
13 #include <string.h>
14 #include <unistd.h>
15 #include <stdarg.h>
16 #include <errno.h>
17 #include <unistd.h>
18 #include <assert.h>
19 #include <signal.h>
20 #include "tools.h"
21
22 static const char *tmpdir = NULL;
23 bool tools_verbose = false;
24
25 /* Ten minutes. */
26 const unsigned int default_timeout_ms = 10 * 60 * 1000;
27
28 char *tal_basename(const void *ctx, const char *dir)
29 {
30         const char *p = strrchr(dir, '/');
31
32         if (!p)
33                 return tal_strdup(ctx, dir);
34         return tal_strdup(ctx, p+1);
35 }
36
37 char *tal_dirname(const void *ctx, const char *dir)
38 {
39         const char *p = strrchr(dir, '/');
40
41         if (!p)
42                 return tal_strdup(ctx, ".");
43         return tal_strndup(ctx, dir, p - dir);
44 }
45
46 char *tal_getcwd(const void *ctx)
47 {
48         unsigned int len;
49         char *cwd;
50
51         /* *This* is why people hate C. */
52         len = 32;
53         cwd = tal_arr(ctx, char, len);
54         while (!getcwd(cwd, len)) {
55                 if (errno != ERANGE) {
56                         tal_free(cwd);
57                         return NULL;
58                 }
59                 tal_resize(&cwd, len *= 2);
60         }
61         return cwd;
62 }
63
64 static void killme(int sig)
65 {
66         kill(-getpid(), SIGKILL);
67 }
68
69 char *run_with_timeout(const void *ctx, const char *cmd,
70                        bool *ok, unsigned *timeout_ms)
71 {
72         pid_t pid;
73         int p[2];
74         struct rbuf in;
75         int status, ms;
76         struct timespec start;
77
78         *ok = false;
79         if (pipe(p) != 0)
80                 return tal_fmt(ctx, "Failed to create pipe: %s",
81                                strerror(errno));
82
83         if (tools_verbose)
84                 printf("Running: %s\n", cmd);
85
86         /* Always flush buffers before fork! */
87         fflush(stdout);
88         start = time_now();
89         pid = fork();
90         if (pid == -1) {
91                 close_noerr(p[0]);
92                 close_noerr(p[1]);
93                 return tal_fmt(ctx, "Failed to fork: %s", strerror(errno));
94         }
95
96         if (pid == 0) {
97                 struct itimerval itim;
98
99                 if (dup2(p[1], STDOUT_FILENO) != STDOUT_FILENO
100                     || dup2(p[1], STDERR_FILENO) != STDERR_FILENO
101                     || close(p[0]) != 0
102                     || close(STDIN_FILENO) != 0
103                     || open("/dev/null", O_RDONLY) != STDIN_FILENO)
104                         exit(128);
105
106                 signal(SIGALRM, killme);
107                 itim.it_interval.tv_sec = itim.it_interval.tv_usec = 0;
108                 itim.it_value = timespec_to_timeval(time_from_msec(*timeout_ms));
109                 setitimer(ITIMER_REAL, &itim, NULL);
110
111                 status = system(cmd);
112                 if (WIFEXITED(status))
113                         exit(WEXITSTATUS(status));
114                 /* Here's a hint... */
115                 exit(128 + WTERMSIG(status));
116         }
117
118         close(p[1]);
119         rbuf_init(&in, p[0], tal_arr(ctx, char, 4096), 4096);
120         if (!rbuf_read_str(&in, 0, do_tal_realloc) && errno)
121                 in.buf = tal_free(in.buf);
122
123         /* This shouldn't fail... */
124         if (waitpid(pid, &status, 0) != pid)
125                 err(1, "Failed to wait for child");
126
127         ms = time_to_msec(time_sub(time_now(), start));
128         if (ms > *timeout_ms)
129                 *timeout_ms = 0;
130         else
131                 *timeout_ms -= ms;
132         close(p[0]);
133         if (tools_verbose) {
134                 printf("%s", in.buf);
135                 printf("Finished: %u ms, %s %u\n", ms,
136                        WIFEXITED(status) ? "exit status" : "killed by signal",
137                        WIFEXITED(status) ? WEXITSTATUS(status)
138                        : WTERMSIG(status));
139         }
140         *ok = (WIFEXITED(status) && WEXITSTATUS(status) == 0);
141         return in.buf;
142 }
143
144 /* Tals *output off ctx; return false if command fails. */
145 bool run_command(const void *ctx, unsigned int *time_ms, char **output,
146                  const char *fmt, ...)
147 {
148         va_list ap;
149         char *cmd;
150         bool ok;
151         unsigned int default_time = default_timeout_ms;
152
153         if (!time_ms)
154                 time_ms = &default_time;
155         else if (*time_ms == 0) {
156                 *output = tal_strdup(ctx, "\n== TIMED OUT ==\n");
157                 return false;
158         }
159
160         va_start(ap, fmt);
161         cmd = tal_vfmt(ctx, fmt, ap);
162         va_end(ap);
163
164         *output = run_with_timeout(ctx, cmd, &ok, time_ms);
165         if (ok)
166                 return true;
167         if (!*output)
168                 err(1, "Problem running child");
169         if (*time_ms == 0)
170                 *output = tal_strcat(ctx, take(*output), "\n== TIMED OUT ==\n");
171         return false;
172 }
173
174 static void unlink_all(const char *dir)
175 {
176         char cmd[strlen(dir) + sizeof("rm -rf ")];
177         sprintf(cmd, "rm -rf %s", dir);
178         if (tools_verbose)
179                 printf("Running: %s\n", cmd);
180         if (system(cmd) != 0)
181                 warn("Could not remove temporary work in %s", dir);
182 }
183
184 static pid_t *afree;
185 static void free_autofree(void)
186 {
187         if (*afree == getpid())
188                 tal_free(afree);
189 }
190
191 tal_t *autofree(void)
192 {
193         if (!afree) {
194                 afree = tal(NULL, pid_t);
195                 *afree = getpid();
196                 atexit(free_autofree);
197         }
198         return afree;
199 }
200
201 const char *temp_dir(void)
202 {
203         /* For first call, create dir. */
204         while (!tmpdir) {
205                 tmpdir = getenv("TMPDIR");
206                 if (!tmpdir)
207                         tmpdir = "/tmp";
208                 tmpdir = tal_fmt(autofree(), "%s/ccanlint-%u.%lu",
209                                  tmpdir, getpid(), random());
210                 if (mkdir(tmpdir, 0700) != 0) {
211                         if (errno == EEXIST) {
212                                 tal_free(tmpdir);
213                                 tmpdir = NULL;
214                                 continue;
215                         }
216                         err(1, "mkdir %s failed", tmpdir);
217                 }
218                 tal_add_destructor(tmpdir, unlink_all);
219                 if (tools_verbose)
220                         printf("Created temporary directory %s\n", tmpdir);
221         }
222         return tmpdir;
223 }
224
225 void keep_temp_dir(void)
226 {
227         tal_del_destructor(temp_dir(), unlink_all);
228 }
229
230 char *temp_file(const void *ctx, const char *extension, const char *srcname)
231 {
232         unsigned baselen;
233         char *f, *suffix = tal_strdup(ctx, "");
234         struct stat st;
235         unsigned int count = 0;
236
237         srcname = tal_basename(ctx, srcname);
238         if (strrchr(srcname, '.'))
239                 baselen = strrchr(srcname, '.') - srcname;
240         else
241                 baselen = strlen(srcname);
242
243         do {
244                 f = tal_fmt(ctx, "%s/%.*s%s%s",
245                             temp_dir(), baselen, srcname, suffix, extension);
246                 tal_free(suffix);
247                 suffix = tal_fmt(ctx, "-%u", ++count);
248         } while (lstat(f, &st) == 0);
249
250         if (tools_verbose)
251                 printf("Creating file %s\n", f);
252
253         tal_free(suffix);
254         return f;
255 }
256
257 bool move_file(const char *oldname, const char *newname)
258 {
259         char *contents;
260         size_t size;
261         int fd;
262         bool ret;
263
264         if (tools_verbose)
265                 printf("Moving file %s to %s: ", oldname, newname);
266
267         /* Simple case: rename works. */
268         if (rename(oldname, newname) == 0) {
269                 if (tools_verbose)
270                         printf("rename worked\n");
271                 return true;
272         }
273
274         /* Try copy and delete: not atomic! */
275         contents = tal_grab_file(NULL, oldname, &size);
276         if (!contents) {
277                 if (tools_verbose)
278                         printf("read failed: %s\n", strerror(errno));
279                 return false;
280         }
281
282         fd = open(newname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
283         if (fd < 0) {
284                 if (tools_verbose)
285                         printf("output open failed: %s\n", strerror(errno));
286                 ret = false;
287                 goto free;
288         }
289
290         ret = write_all(fd, contents, size);
291         if (close(fd) != 0)
292                 ret = false;
293
294         if (ret) {
295                 if (tools_verbose)
296                         printf("copy worked\n");
297                 unlink(oldname);
298         } else {
299                 if (tools_verbose)
300                         printf("write/close failed\n");
301                 unlink(newname);
302         }
303
304 free:
305         tal_free(contents);
306         return ret;
307 }
308
309 void *do_tal_realloc(void *p, size_t size)
310 {
311         tal_resize((char **)&p, size);
312         return p;
313 }
314
315 void *tal_grab_file(const void *ctx, const char *filename, size_t *size)
316 {
317         struct rbuf rbuf;
318         char *buf = tal_arr(ctx, char, 0);
319
320         if (!rbuf_open(&rbuf, filename, buf, 0))
321                 return tal_free(buf);
322
323         if (!rbuf_fill_all(&rbuf, do_tal_realloc) && errno)
324                 rbuf.buf = tal_free(rbuf.buf);
325         else {
326                 rbuf.buf[rbuf.len] = '\0';
327                 if (size)
328                         *size = rbuf.len;
329         }
330         close(rbuf.fd);
331
332         return rbuf.buf;
333 }