]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - usr.bin/csup/misc.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / usr.bin / 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
32 #include <assert.h>
33 #include <err.h>
34 #include <errno.h>
35 #include <fcntl.h>
36 #include <limits.h>
37 #include <pthread.h>
38 #include <stdarg.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <time.h>
43 #include <unistd.h>
44
45 #include "fattr.h"
46 #include "main.h"
47 #include "misc.h"
48
49 struct pattlist {
50         char **patterns;
51         size_t size;
52         size_t in;
53 };
54
55 struct backoff_timer {
56         time_t min;
57         time_t max;
58         time_t interval;
59         float backoff;
60         float jitter;
61 };
62
63 static void     bt_update(struct backoff_timer *);
64 static void     bt_addjitter(struct backoff_timer *);
65
66 int
67 asciitoint(const char *s, int *val, int base)
68 {
69         char *end;
70         long longval;
71
72         errno = 0;
73         longval = strtol(s, &end, base);
74         if (errno || *end != '\0')
75                 return (-1);
76         if (longval > INT_MAX || longval < INT_MIN) {
77                 errno = ERANGE;
78                 return (-1);
79         }
80         *val = longval;
81         return (0);
82 }
83
84 int
85 lprintf(int level, const char *fmt, ...)
86 {
87         FILE *to;
88         va_list ap;
89         int ret;
90
91         if (level > verbose)
92                 return (0);
93         if (level == -1)
94                 to = stderr;
95         else
96                 to = stdout;
97         va_start(ap, fmt);
98         ret = vfprintf(to, fmt, ap);
99         va_end(ap);
100         fflush(to);
101         return (ret);
102 }
103
104 /*
105  * Compute the MD5 checksum of a file.  The md parameter must
106  * point to a buffer containing at least MD5_DIGEST_SIZE bytes.
107  *
108  * Do not confuse OpenSSL's MD5_DIGEST_LENGTH with our own
109  * MD5_DIGEST_SIZE macro.
110  */
111 int
112 MD5_File(char *path, char *md)
113 {
114         char buf[1024];
115         MD5_CTX ctx;
116         ssize_t n;
117         int fd;
118
119         fd = open(path, O_RDONLY);
120         if (fd == -1)
121                 return (-1);
122         MD5_Init(&ctx);
123         while ((n = read(fd, buf, sizeof(buf))) > 0)
124                 MD5_Update(&ctx, buf, n);
125         close(fd);
126         if (n == -1)
127                 return (-1);
128         MD5_End(md, &ctx);
129         return (0);
130 }
131
132 /*
133  * Wrapper around MD5_Final() that converts the 128 bits MD5 hash
134  * to an ASCII string representing this value in hexadecimal.
135  */
136 void
137 MD5_End(char *md, MD5_CTX *c)
138 {
139         unsigned char md5[MD5_DIGEST_LENGTH];
140         const char hex[] = "0123456789abcdef";
141         int i, j;
142
143         MD5_Final(md5, c);
144         j = 0;
145         for (i = 0; i < MD5_DIGEST_LENGTH; i++) {
146                 md[j++] = hex[md5[i] >> 4];
147                 md[j++] = hex[md5[i] & 0xf];
148         }
149         md[j] = '\0';
150 }
151
152 int
153 pathcmp(const char *s1, const char *s2)
154 {
155         char c1, c2;
156
157         do {
158                 c1 = *s1++;
159                 if (c1 == '/')
160                         c1 = 1;
161                 c2 = *s2++;
162                 if (c2 == '/')
163                         c2 = 1;
164         } while (c1 == c2 && c1 != '\0');
165
166         return (c1 - c2);
167 }
168
169 size_t
170 commonpathlength(const char *a, size_t alen, const char *b, size_t blen)
171 {
172         size_t i, minlen, lastslash;
173
174         minlen = min(alen, blen);
175         lastslash = 0;
176         for (i = 0; i < minlen; i++) {
177                 if (a[i] != b[i])
178                         return (lastslash);
179                 if (a[i] == '/') {
180                         if (i == 0)     /* Include the leading slash. */
181                                 lastslash = 1;
182                         else
183                                 lastslash = i;
184                 }
185         }
186
187         /* One path is a prefix of the other/ */
188         if (alen > minlen) {            /* Path "b" is a prefix of "a". */
189                 if (a[minlen] == '/')
190                         return (minlen);
191                 else
192                         return (lastslash);
193         } else if (blen > minlen) {     /* Path "a" is a prefix of "b". */
194                 if (b[minlen] == '/')
195                         return (minlen);
196                 else
197                         return (lastslash);
198         }
199
200         /* The paths are identical. */
201         return (minlen);
202 }
203
204 const char *
205 pathlast(const char *path)
206 {
207         const char *s;
208
209         s = strrchr(path, '/');
210         if (s == NULL)
211                 return (path);
212         return (++s);
213 }
214
215 int
216 rcsdatetotm(const char *revdate, struct tm *tm)
217 {
218         char *cp;
219         size_t len;
220
221         cp = strchr(revdate, '.');
222         if (cp == NULL)
223                 return (-1);
224         len = cp - revdate;
225         if (len >= 4)
226                 cp = strptime(revdate, "%Y.%m.%d.%H.%M.%S", tm);
227         else if (len == 2)
228                 cp = strptime(revdate, "%y.%m.%d.%H.%M.%S", tm);
229         else
230                 return (-1);
231         if (cp == NULL || *cp != '\0')
232                 return (-1);
233         return (0);
234 }
235
236 time_t
237 rcsdatetotime(const char *revdate)
238 {
239         struct tm tm;
240         time_t t;
241         int error;
242
243         error = rcsdatetotm(revdate, &tm);
244         if (error)
245                 return (error);
246         t = timegm(&tm);
247         return (t);
248 }
249
250 /*
251  * Checks if a file is an RCS file.
252  */
253 int
254 isrcs(const char *file, size_t *len)
255 {
256         const char *cp;
257
258         if (file[0] == '/')
259                 return (0);
260         cp = file;
261         while ((cp = strstr(cp, "..")) != NULL) {
262                 if (cp == file || cp[2] == '\0' ||
263                     (cp[-1] == '/' && cp[2] == '/'))
264                         return (0);
265                 cp += 2;
266         }
267         *len = strlen(file);
268         if (*len < 2 || file[*len - 1] != 'v' || file[*len - 2] != ',') {
269                 return (0);
270         }
271
272         return (1);
273 }
274
275 /*
276  * Returns a buffer allocated with malloc() containing the absolute
277  * pathname to the checkout file made from the prefix and the path
278  * of the corresponding RCS file relatively to the prefix.  If the
279  * filename is not an RCS filename, NULL will be returned.
280  */
281 char *
282 checkoutpath(const char *prefix, const char *file)
283 {
284         char *path;
285         size_t len;
286
287         if (!isrcs(file, &len))
288                 return (NULL);
289         xasprintf(&path, "%s/%.*s", prefix, (int)len - 2, file);
290         return (path);
291 }
292
293 /*
294  * Returns a cvs path allocated with malloc() containing absolute pathname to a
295  * file in cvs mode which can reside in the attic. XXX: filename has really no
296  * restrictions.
297  */
298 char *
299 cvspath(const char *prefix, const char *file, int attic)
300 {
301         const char *last;
302         char *path;
303
304         last = pathlast(file);
305         if (attic)
306                 xasprintf(&path, "%s/%.*sAttic/%s", prefix, (int)(last - file),
307                     file, last);
308         else
309                 xasprintf(&path, "%s/%s", prefix, file);
310
311         return (path);
312 }
313
314 /*
315  * Regular or attic path if regular fails.
316  * XXX: This should perhaps also check if the Attic file exists too, and return
317  * NULL if not.
318  */
319 char *
320 atticpath(const char *prefix, const char *file)
321 {
322         char *path;
323
324         path = cvspath(prefix, file, 0);
325         if (access(path, F_OK) != 0) {
326                 free(path);
327                 path = cvspath(prefix, file, 1);
328         }
329         return (path);
330 }
331
332 int
333 mkdirhier(char *path, mode_t mask)
334 {
335         struct fattr *fa;
336         size_t i, last, len;
337         int error, finish, rv;
338
339         finish = 0;
340         last = 0;
341         len = strlen(path);
342         for (i = len - 1; i > 0; i--) {
343                 if (path[i] == '/') {
344                         path[i] = '\0';
345                         if (access(path, F_OK) == 0) {
346                                 path[i] = '/';
347                                 break;
348                         }
349                         if (errno != ENOENT) {
350                                 path[i] = '/';
351                                 if (last == 0)
352                                         return (-1);
353                                 finish = 1;
354                                 break;
355                         }
356                         last = i;
357                 }
358         }
359         if (last == 0)
360                 return (0);
361
362         i = strlen(path);
363         fa = fattr_new(FT_DIRECTORY, -1);
364         fattr_mergedefault(fa);
365         fattr_umask(fa, mask);
366         while (i < len) {
367                 if (!finish) {
368                         rv = 0;
369                         error = fattr_makenode(fa, path);
370                         if (!error)
371                                 rv = fattr_install(fa, path, NULL);
372                         if (error || rv == -1)
373                                 finish = 1;
374                 }
375                 path[i] = '/';
376                 i += strlen(path + i);
377         }
378         assert(i == len);
379         if (finish)
380                 return (-1);
381         return (0);
382 }
383
384 /*
385  * Compute temporary pathnames.
386  * This can look a bit like overkill but we mimic CVSup's behaviour.
387  */
388 #define TEMPNAME_PREFIX         "#cvs.csup"
389
390 static pthread_mutex_t tempname_mtx = PTHREAD_MUTEX_INITIALIZER;
391 static pid_t tempname_pid = -1;
392 static int tempname_count;
393
394 char *
395 tempname(const char *path)
396 {
397         char *cp, *temp;
398         int count, error;
399
400         error = pthread_mutex_lock(&tempname_mtx);
401         assert(!error);
402         if (tempname_pid == -1) {
403                 tempname_pid = getpid();
404                 tempname_count = 0;
405         }
406         count = tempname_count++;
407         error = pthread_mutex_unlock(&tempname_mtx);
408         assert(!error);
409         cp = strrchr(path, '/');
410         if (cp == NULL)
411                 xasprintf(&temp, "%s-%ld.%d", TEMPNAME_PREFIX,
412                     (long)tempname_pid, count);
413         else
414                 xasprintf(&temp, "%.*s%s-%ld.%d", (int)(cp - path + 1), path,
415                     TEMPNAME_PREFIX, (long)tempname_pid, count);
416         return (temp);
417 }
418
419 void *
420 xmalloc(size_t size)
421 {
422         void *buf;
423
424         buf = malloc(size);
425         if (buf == NULL)
426                 err(1, "malloc");
427         return (buf);
428 }
429
430 void *
431 xrealloc(void *buf, size_t size)
432 {
433
434         buf = realloc(buf, size);
435         if (buf == NULL)
436                 err(1, "realloc");
437         return (buf);
438 }
439
440 char *
441 xstrdup(const char *str)
442 {
443         char *buf;
444
445         buf = strdup(str);
446         if (buf == NULL)
447                 err(1, "strdup");
448         return (buf);
449 }
450
451 int
452 xasprintf(char **ret, const char *format, ...)
453 {
454         va_list ap;
455         int rv;
456
457         va_start(ap, format);
458         rv = vasprintf(ret, format, ap);
459         va_end(ap);
460         if (*ret == NULL)
461                 err(1, "asprintf");
462         return (rv);
463 }
464
465 struct pattlist *
466 pattlist_new(void)
467 {
468         struct pattlist *p;
469
470         p = xmalloc(sizeof(struct pattlist));
471         p->size = 4;            /* Initial size. */
472         p->patterns = xmalloc(p->size * sizeof(char *));
473         p->in = 0;
474         return (p);
475 }
476
477 void
478 pattlist_add(struct pattlist *p, const char *pattern)
479 {
480
481         if (p->in == p->size) {
482                 p->size *= 2;
483                 p->patterns = xrealloc(p->patterns, p->size * sizeof(char *));
484         }
485         assert(p->in < p->size);
486         p->patterns[p->in++] = xstrdup(pattern);
487 }
488
489 char *
490 pattlist_get(struct pattlist *p, size_t i)
491 {
492
493         assert(i < p->in);
494         return (p->patterns[i]);
495 }
496
497 size_t
498 pattlist_size(struct pattlist *p)
499 {
500
501         return (p->in);
502 }
503
504 void
505 pattlist_free(struct pattlist *p)
506 {
507         size_t i;
508         
509         for (i = 0; i < p->in; i++)
510                 free(p->patterns[i]);
511         free(p->patterns);
512         free(p);
513 }
514
515 /* Creates a backoff timer. */
516 struct backoff_timer *
517 bt_new(time_t min, time_t max, float backoff, float jitter)
518 {
519         struct backoff_timer *bt;
520
521         bt = xmalloc(sizeof(struct backoff_timer));
522         bt->min = min;
523         bt->max = max;
524         bt->backoff = backoff;
525         bt->jitter = jitter;
526         bt->interval = min;
527         bt_addjitter(bt);
528         srandom(time(0));
529         return (bt);
530 }
531
532 /* Updates the backoff timer. */
533 static void
534 bt_update(struct backoff_timer *bt)
535 {
536
537         bt->interval = (time_t)min(bt->interval * bt->backoff, bt->max);
538         bt_addjitter(bt);
539 }
540
541 /* Adds some jitter. */
542 static void
543 bt_addjitter(struct backoff_timer *bt)
544 {
545         long mag;
546
547         mag = (long)(bt->jitter * bt->interval);
548         /* We want a random number between -mag and mag. */
549         bt->interval += (time_t)(random() % (2 * mag) - mag);
550 }
551
552 /* Returns the current timer value. */
553 time_t
554 bt_get(struct backoff_timer *bt)
555 {
556
557         return (bt->interval);
558 }
559
560 /* Times out for bt->interval seconds. */
561 void
562 bt_pause(struct backoff_timer *bt)
563 {
564
565         sleep(bt->interval);
566         bt_update(bt);
567 }
568
569 void
570 bt_free(struct backoff_timer *bt)
571 {
572
573         free(bt);
574 }
575
576 /* Compare two revisions. */
577 int
578 rcsnum_cmp(char *revision1, char *revision2)
579 {
580         char *ptr1, *ptr2, *dot1, *dot2;
581         int num1len, num2len, ret;
582
583         ptr1 = revision1;
584         ptr2 = revision2;
585         while (*ptr1 != '\0' && *ptr2 != '\0') {
586                 dot1 = strchr(ptr1, '.');
587                 dot2 = strchr(ptr2, '.');
588                 if (dot1 == NULL)
589                         dot1 = strchr(ptr1, '\0');
590                 if (dot2 == NULL)
591                         dot2 = strchr(ptr2, '\0');
592
593                 num1len = dot1 - ptr1;
594                 num2len = dot2 - ptr2;
595                 /* Check the distance between each, showing how many digits */
596                 if (num1len > num2len)
597                         return (1);
598                 else if (num1len < num2len)
599                         return (-1);
600
601                 /* Equal distance means we must check each character. */
602                 ret = strncmp(ptr1, ptr2, num1len);
603                 if (ret != 0)
604                         return (ret);
605                 ptr1 = (*dot1 == '.') ? (dot1 + 1) : dot1;
606                 ptr2 = (*dot2 == '.') ? (dot2 + 1) : dot2;
607         } 
608
609         if (*ptr1 != '\0' && *ptr2 == '\0')
610                 return (1);
611         if (*ptr1 == '\0' && *ptr2 != '\0')
612                 return (-1);
613         return (0);
614
615 }
616
617 /* Returns 0 if a rcsrev is not a trunk revision number. */
618 int
619 rcsrev_istrunk(char *revnum)
620 {
621         char *tmp;
622
623         tmp = strchr(revnum, '.');
624         tmp++;
625         if (strchr(tmp, '.') != NULL)
626                 return (0);
627         return (1);
628 }
629
630 /* Return prefix of rcsfile. */
631 char *
632 rcsrev_prefix(char *revnum)
633 {
634         char *modrev, *pos;
635
636         modrev = xstrdup(revnum);
637         pos = strrchr(modrev, '.');
638         if (pos == NULL) {
639                 free(modrev);
640                 return (NULL);
641         }
642         *pos = '\0';
643         return (modrev);
644 }