]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/csup/misc.c
This commit was generated by cvs2svn to compensate for changes in r164146,
[FreeBSD/FreeBSD.git] / contrib / csup / misc.c
1 /*-
2  * Copyright (c) 2003-2006, Maxime Henrion <mux@FreeBSD.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  */
28
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <openssl/md5.h>
32
33 #include <assert.h>
34 #include <err.h>
35 #include <errno.h>
36 #include <fcntl.h>
37 #include <limits.h>
38 #include <pthread.h>
39 #include <stdarg.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <time.h>
44 #include <unistd.h>
45
46 #include "fattr.h"
47 #include "main.h"
48 #include "misc.h"
49
50 struct pattlist {
51         char **patterns;
52         size_t size;
53         size_t in;
54 };
55
56 struct backoff_timer {
57         time_t min;
58         time_t max;
59         time_t interval;
60         float backoff;
61         float jitter;
62 };
63
64 static void     bt_update(struct backoff_timer *);
65 static void     bt_addjitter(struct backoff_timer *);
66
67 int
68 asciitoint(const char *s, int *val, int base)
69 {
70         char *end;
71         long longval;
72
73         errno = 0;
74         longval = strtol(s, &end, base);
75         if (errno || *end != '\0')
76                 return (-1);
77         if (longval > INT_MAX || longval < INT_MIN) {
78                 errno = ERANGE;
79                 return (-1);
80         }
81         *val = longval;
82         return (0);
83 }
84
85 int
86 lprintf(int level, const char *fmt, ...)
87 {
88         FILE *to;
89         va_list ap;
90         int ret;
91
92         if (level > verbose)
93                 return (0);
94         if (level == -1)
95                 to = stderr;
96         else
97                 to = stdout;
98         va_start(ap, fmt);
99         ret = vfprintf(to, fmt, ap);
100         va_end(ap);
101         fflush(to);
102         return (ret);
103 }
104
105 /*
106  * Compute the MD5 checksum of a file.  The md parameter must
107  * point to a buffer containing at least MD5_DIGEST_SIZE bytes.
108  *
109  * Do not confuse OpenSSL's MD5_DIGEST_LENGTH with our own
110  * MD5_DIGEST_SIZE macro.
111  */
112 int
113 MD5_File(char *path, char *md)
114 {
115         char buf[1024];
116         MD5_CTX ctx;
117         ssize_t n;
118         int fd;
119
120         fd = open(path, O_RDONLY);
121         if (fd == -1)
122                 return (-1);
123         MD5_Init(&ctx);
124         while ((n = read(fd, buf, sizeof(buf))) > 0)
125                 MD5_Update(&ctx, buf, n);
126         close(fd);
127         if (n == -1)
128                 return (-1);
129         MD5_End(md, &ctx);
130         return (0);
131 }
132
133 /*
134  * Wrapper around MD5_Final() that converts the 128 bits MD5 hash
135  * to an ASCII string representing this value in hexadecimal.
136  */
137 void
138 MD5_End(char *md, MD5_CTX *c)
139 {
140         unsigned char md5[MD5_DIGEST_LENGTH];
141         const char hex[] = "0123456789abcdef";
142         int i, j;
143
144         MD5_Final(md5, c);
145         j = 0;
146         for (i = 0; i < MD5_DIGEST_LENGTH; i++) {
147                 md[j++] = hex[md5[i] >> 4];
148                 md[j++] = hex[md5[i] & 0xf];
149         }
150         md[j] = '\0';
151 }
152
153 int
154 pathcmp(const char *s1, const char *s2)
155 {
156         char c1, c2;
157
158         do {
159                 c1 = *s1++;
160                 if (c1 == '/')
161                         c1 = 1;
162                 c2 = *s2++;
163                 if (c2 == '/')
164                         c2 = 1;
165         } while (c1 == c2 && c1 != '\0');
166
167         return (c1 - c2);
168 }
169
170 size_t
171 commonpathlength(const char *a, size_t alen, const char *b, size_t blen)
172 {
173         size_t i, minlen, lastslash;
174
175         minlen = min(alen, blen);
176         lastslash = 0;
177         for (i = 0; i < minlen; i++) {
178                 if (a[i] != b[i])
179                         return (lastslash);
180                 if (a[i] == '/') {
181                         if (i == 0)     /* Include the leading slash. */
182                                 lastslash = 1;
183                         else
184                                 lastslash = i;
185                 }
186         }
187
188         /* One path is a prefix of the other/ */
189         if (alen > minlen) {            /* Path "b" is a prefix of "a". */
190                 if (a[minlen] == '/')
191                         return (minlen);
192                 else
193                         return (lastslash);
194         } else if (blen > minlen) {     /* Path "a" is a prefix of "b". */
195                 if (b[minlen] == '/')
196                         return (minlen);
197                 else
198                         return (lastslash);
199         }
200
201         /* The paths are identical. */
202         return (minlen);
203 }
204
205 char *
206 pathlast(char *path)
207 {
208         char *s;
209
210         s = strrchr(path, '/');
211         if (s == NULL)
212                 return (path);
213         return (++s);
214 }
215
216 int
217 rcsdatetotm(const char *revdate, struct tm *tm)
218 {
219         char *cp;
220         size_t len;
221
222         cp = strchr(revdate, '.');
223         if (cp == NULL)
224                 return (-1);
225         len = cp - revdate;
226         if (len >= 4)
227                 cp = strptime(revdate, "%Y.%m.%d.%H.%M.%S", tm);
228         else if (len == 2)
229                 cp = strptime(revdate, "%y.%m.%d.%H.%M.%S", tm);
230         else
231                 return (-1);
232         if (cp == NULL || *cp != '\0')
233                 return (-1);
234         return (0);
235 }
236
237 time_t
238 rcsdatetotime(const char *revdate)
239 {
240         struct tm tm;
241         time_t t;
242         int error;
243
244         error = rcsdatetotm(revdate, &tm);
245         if (error)
246                 return (error);
247         t = timegm(&tm);
248         return (t);
249 }
250
251 /*
252  * Returns a buffer allocated with malloc() containing the absolute
253  * pathname to the checkout file made from the prefix and the path
254  * of the corresponding RCS file relatively to the prefix.  If the
255  * filename is not an RCS filename, NULL will be returned.
256  */
257 char *
258 checkoutpath(const char *prefix, const char *file)
259 {
260         const char *cp;
261         char *path;
262         size_t len;
263
264         if (file[0] == '/')
265                 return (NULL);
266         cp = file;
267         while ((cp = strstr(cp, "..")) != NULL) {
268                 if (cp == file || cp[2] == '\0' ||
269                     (cp[-1] == '/' && cp[2] == '/'))
270                         return (NULL);
271                 cp += 2;
272         }
273         len = strlen(file);
274         if (len < 2 || file[len - 1] != 'v' || file[len - 2] != ',')
275                 return (NULL);
276         xasprintf(&path, "%s/%.*s", prefix, (int)len - 2, file);
277         return (path);
278 }
279
280 int
281 mkdirhier(char *path, mode_t mask)
282 {
283         struct fattr *fa;
284         size_t i, last, len;
285         int error, finish, rv;
286
287         finish = 0;
288         last = 0;
289         len = strlen(path);
290         for (i = len - 1; i > 0; i--) {
291                 if (path[i] == '/') {
292                         path[i] = '\0';
293                         if (access(path, F_OK) == 0) {
294                                 path[i] = '/';
295                                 break;
296                         }
297                         if (errno != ENOENT) {
298                                 path[i] = '/';
299                                 if (last == 0)
300                                         return (-1);
301                                 finish = 1;
302                                 break;
303                         }
304                         last = i;
305                 }
306         }
307         if (last == 0)
308                 return (0);
309
310         i = strlen(path);
311         fa = fattr_new(FT_DIRECTORY, -1);
312         fattr_mergedefault(fa);
313         fattr_umask(fa, mask);
314         while (i < len) {
315                 if (!finish) {
316                         rv = 0;
317                         error = fattr_makenode(fa, path);
318                         if (!error)
319                                 rv = fattr_install(fa, path, NULL);
320                         if (error || rv == -1)
321                                 finish = 1;
322                 }
323                 path[i] = '/';
324                 i += strlen(path + i);
325         }
326         assert(i == len);
327         if (finish)
328                 return (-1);
329         return (0);
330 }
331
332 /*
333  * Compute temporary pathnames.
334  * This can look a bit like overkill but we mimic CVSup's behaviour.
335  */
336 #define TEMPNAME_PREFIX         "#cvs.csup"
337
338 static pthread_mutex_t tempname_mtx = PTHREAD_MUTEX_INITIALIZER;
339 static pid_t tempname_pid = -1;
340 static int tempname_count;
341
342 char *
343 tempname(const char *path)
344 {
345         char *cp, *temp;
346         int count, error;
347
348         error = pthread_mutex_lock(&tempname_mtx);
349         assert(!error);
350         if (tempname_pid == -1) {
351                 tempname_pid = getpid();
352                 tempname_count = 0;
353         }
354         count = tempname_count++;
355         error = pthread_mutex_unlock(&tempname_mtx);
356         assert(!error);
357         cp = strrchr(path, '/');
358         if (cp == NULL)
359                 xasprintf(&temp, "%s-%ld.%d", TEMPNAME_PREFIX,
360                     (long)tempname_pid, count);
361         else
362                 xasprintf(&temp, "%.*s%s-%ld.%d", (int)(cp - path + 1), path,
363                     TEMPNAME_PREFIX, (long)tempname_pid, count);
364         return (temp);
365 }
366
367 void *
368 xmalloc(size_t size)
369 {
370         void *buf;
371
372         buf = malloc(size);
373         if (buf == NULL)
374                 err(1, "malloc");
375         return (buf);
376 }
377
378 void *
379 xrealloc(void *buf, size_t size)
380 {
381
382         buf = realloc(buf, size);
383         if (buf == NULL)
384                 err(1, "realloc");
385         return (buf);
386 }
387
388 char *
389 xstrdup(const char *str)
390 {
391         char *buf;
392
393         buf = strdup(str);
394         if (buf == NULL)
395                 err(1, "strdup");
396         return (buf);
397 }
398
399 int
400 xasprintf(char **ret, const char *format, ...)
401 {
402         va_list ap;
403         int rv;
404
405         va_start(ap, format);
406         rv = vasprintf(ret, format, ap);
407         va_end(ap);
408         if (*ret == NULL)
409                 err(1, "asprintf");
410         return (rv);
411 }
412
413 struct pattlist *
414 pattlist_new(void)
415 {
416         struct pattlist *p;
417
418         p = xmalloc(sizeof(struct pattlist));
419         p->size = 4;            /* Initial size. */
420         p->patterns = xmalloc(p->size * sizeof(char *));
421         p->in = 0;
422         return (p);
423 }
424
425 void
426 pattlist_add(struct pattlist *p, const char *pattern)
427 {
428
429         if (p->in == p->size) {
430                 p->size *= 2;
431                 p->patterns = xrealloc(p->patterns, p->size * sizeof(char *));
432         }
433         assert(p->in < p->size);
434         p->patterns[p->in++] = xstrdup(pattern);
435 }
436
437 char *
438 pattlist_get(struct pattlist *p, size_t i)
439 {
440
441         assert(i < p->in);
442         return (p->patterns[i]);
443 }
444
445 size_t
446 pattlist_size(struct pattlist *p)
447 {
448
449         return (p->in);
450 }
451
452 void
453 pattlist_free(struct pattlist *p)
454 {
455         size_t i;
456         
457         for (i = 0; i < p->in; i++)
458                 free(p->patterns[i]);
459         free(p->patterns);
460         free(p);
461 }
462
463 /* Creates a backoff timer. */
464 struct backoff_timer *
465 bt_new(time_t min, time_t max, float backoff, float jitter)
466 {
467         struct backoff_timer *bt;
468
469         bt = xmalloc(sizeof(struct backoff_timer));
470         bt->min = min;
471         bt->max = max;
472         bt->backoff = backoff;
473         bt->jitter = jitter;
474         bt->interval = min;
475         bt_addjitter(bt);
476         srandom(time(0));
477         return (bt);
478 }
479
480 /* Updates the backoff timer. */
481 static void
482 bt_update(struct backoff_timer *bt)
483 {
484
485         bt->interval = (time_t)min(bt->interval * bt->backoff, bt->max);
486         bt_addjitter(bt);
487 }
488
489 /* Adds some jitter. */
490 static void
491 bt_addjitter(struct backoff_timer *bt)
492 {
493         long mag;
494
495         mag = (long)(bt->jitter * bt->interval);
496         /* We want a random number between -mag and mag. */
497         bt->interval += (time_t)(random() % (2 * mag) - mag);
498 }
499
500 /* Returns the current timer value. */
501 time_t
502 bt_get(struct backoff_timer *bt)
503 {
504
505         return (bt->interval);
506 }
507
508 /* Times out for bt->interval seconds. */
509 void
510 bt_pause(struct backoff_timer *bt)
511 {
512
513         sleep(bt->interval);
514         bt_update(bt);
515 }
516
517 void
518 bt_free(struct backoff_timer *bt)
519 {
520
521         free(bt);
522 }