Doc.
[rsync/rsync.git] / util.c
1 /*  -*- c-file-style: "linux" -*-
2  * 
3  * Copyright (C) 1996-2000 by Andrew Tridgell 
4  * Copyright (C) Paul Mackerras 1996
5  * Copyright (C) 2001, 2002 by Martin Pool <mbp@samba.org>
6  * 
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  * 
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20  */
21
22 /**
23  * @file
24  *
25  * Utilities used in rsync 
26  **/
27
28 #include "rsync.h"
29
30 extern int verbose;
31
32 int sanitize_paths = 0;
33
34
35
36 /**
37  * Set a fd into nonblocking mode
38  **/
39 void set_nonblocking(int fd)
40 {
41         int val;
42
43         if ((val = fcntl(fd, F_GETFL, 0)) == -1)
44                 return;
45         if (!(val & NONBLOCK_FLAG)) {
46                 val |= NONBLOCK_FLAG;
47                 fcntl(fd, F_SETFL, val);
48         }
49 }
50
51 /**
52  * Set a fd into blocking mode
53  **/
54 void set_blocking(int fd)
55 {
56         int val;
57
58         if ((val = fcntl(fd, F_GETFL, 0)) == -1)
59                 return;
60         if (val & NONBLOCK_FLAG) {
61                 val &= ~NONBLOCK_FLAG;
62                 fcntl(fd, F_SETFL, val);
63         }
64 }
65
66
67 /**
68  * Create a file descriptor pair - like pipe() but use socketpair if
69  * possible (because of blocking issues on pipes).
70  * 
71  * Always set non-blocking.
72  */
73 int fd_pair(int fd[2])
74 {
75         int ret;
76
77 #if HAVE_SOCKETPAIR
78         ret = socketpair(AF_UNIX, SOCK_STREAM, 0, fd);
79 #else
80         ret = pipe(fd);
81 #endif
82
83         if (ret == 0) {
84                 set_nonblocking(fd[0]);
85                 set_nonblocking(fd[1]);
86         }
87
88         return ret;
89 }
90
91
92 void print_child_argv(char **cmd)
93 {
94         rprintf(FINFO, "opening connection using ");
95         for (; *cmd; cmd++) {
96                 /* Look for characters that ought to be quoted.  This
97                 * is not a great quoting algorithm, but it's
98                 * sufficient for a log message. */
99                 if (strspn(*cmd, "abcdefghijklmnopqrstuvwxyz"
100                            "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
101                            "0123456789"
102                            ",.-_=+@/") != strlen(*cmd)) {
103                         rprintf(FINFO, "\"%s\" ", *cmd);
104                 } else {
105                         rprintf(FINFO, "%s ", *cmd);
106                 }
107         }
108         rprintf(FINFO, "\n");
109 }
110
111
112 void out_of_memory(char *str)
113 {
114   rprintf(FERROR,"ERROR: out of memory in %s\n",str);
115   exit_cleanup(RERR_MALLOC);
116 }
117
118 void overflow(char *str)
119 {
120   rprintf(FERROR,"ERROR: buffer overflow in %s\n",str);
121   exit_cleanup(RERR_MALLOC);
122 }
123
124
125
126 int set_modtime(char *fname, time_t modtime)
127 {
128         extern int dry_run;
129         if (dry_run)
130                 return 0;
131
132         if (verbose > 2) {
133                 rprintf(FINFO, "set modtime of %s to (%ld) %s",
134                         fname, (long) modtime,
135                         asctime(localtime(&modtime)));
136         }
137         
138         {
139 #ifdef HAVE_UTIMBUF
140                 struct utimbuf tbuf;  
141                 tbuf.actime = time(NULL);
142                 tbuf.modtime = modtime;
143                 return utime(fname,&tbuf);
144 #elif defined(HAVE_UTIME)
145                 time_t t[2];
146                 t[0] = time(NULL);
147                 t[1] = modtime;
148                 return utime(fname,t);
149 #else
150                 struct timeval t[2];
151                 t[0].tv_sec = time(NULL);
152                 t[0].tv_usec = 0;
153                 t[1].tv_sec = modtime;
154                 t[1].tv_usec = 0;
155                 return utimes(fname,t);
156 #endif
157         }
158 }
159
160
161 /**
162    Create any necessary directories in fname. Unfortunately we don't know
163    what perms to give the directory when this is called so we need to rely
164    on the umask
165 **/
166 int create_directory_path(char *fname, int base_umask)
167 {
168         char *p;
169
170         while (*fname == '/') fname++;
171         while (strncmp(fname,"./",2)==0) fname += 2;
172
173         p = fname;
174         while ((p=strchr(p,'/'))) {
175                 *p = 0;
176                 do_mkdir(fname, 0777 & ~base_umask); 
177                 *p = '/';
178                 p++;
179         }
180         return 0;
181 }
182
183
184 /**
185  * Write @p len bytes at @p ptr to descriptor @p desc, retrying if
186  * interrupted.
187  *
188  * @retval len upon success
189  *
190  * @retval <0 write's (negative) error code
191  *
192  * Derived from GNU C's cccp.c.
193  */
194 static int full_write(int desc, char *ptr, size_t len)
195 {
196         int total_written;
197         
198         total_written = 0;
199         while (len > 0) {
200                 int written = write (desc, ptr, len);
201                 if (written < 0)  {
202 #ifdef EINTR
203                         if (errno == EINTR)
204                                 continue;
205 #endif
206                         return written;
207                 }
208                 total_written += written;
209                 ptr += written;
210                 len -= written;
211         }
212         return total_written;
213 }
214
215
216 /**
217  * Read @p len bytes at @p ptr from descriptor @p desc, retrying if
218  * interrupted.
219  *
220  * @retval >0 the actual number of bytes read
221  *
222  * @retval 0 for EOF
223  *
224  * @retval <0 for an error.
225  *
226  * Derived from GNU C's cccp.c. */
227 static int safe_read(int desc, char *ptr, size_t len)
228 {
229         int n_chars;
230  
231         if (len == 0)
232                 return len;
233  
234 #ifdef EINTR
235         do {
236                 n_chars = read(desc, ptr, len);
237         } while (n_chars < 0 && errno == EINTR);
238 #else
239         n_chars = read(desc, ptr, len);
240 #endif
241  
242         return n_chars;
243 }
244
245
246 /** Copy a file.
247  *
248  * This is used in conjunction with the --temp-dir option */
249 int copy_file(char *source, char *dest, mode_t mode)
250 {
251         int ifd;
252         int ofd;
253         char buf[1024 * 8];
254         int len;   /* Number of bytes read into `buf'. */
255
256         ifd = do_open(source, O_RDONLY, 0);
257         if (ifd == -1) {
258                 rprintf(FERROR,"open %s: %s\n",
259                         source,strerror(errno));
260                 return -1;
261         }
262
263         if (robust_unlink(dest) && errno != ENOENT) {
264                 rprintf(FERROR,"unlink %s: %s\n",
265                         dest,strerror(errno));
266                 return -1;
267         }
268
269         ofd = do_open(dest, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, mode);
270         if (ofd == -1) {
271                 rprintf(FERROR,"open %s: %s\n",
272                         dest,strerror(errno));
273                 close(ifd);
274                 return -1;
275         }
276
277         while ((len = safe_read(ifd, buf, sizeof(buf))) > 0) {
278                 if (full_write(ofd, buf, len) < 0) {
279                         rprintf(FERROR,"write %s: %s\n",
280                                 dest,strerror(errno));
281                         close(ifd);
282                         close(ofd);
283                         return -1;
284                 }
285         }
286
287         close(ifd);
288         close(ofd);
289
290         if (len < 0) {
291                 rprintf(FERROR,"read %s: %s\n",
292                         source,strerror(errno));
293                 return -1;
294         }
295
296         return 0;
297 }
298
299 /* MAX_RENAMES should be 10**MAX_RENAMES_DIGITS */
300 #define MAX_RENAMES_DIGITS 3
301 #define MAX_RENAMES 1000
302
303 /**
304  * Robust unlink: some OS'es (HPUX) refuse to unlink busy files, so
305  * rename to <path>/.rsyncNNN instead.
306  *
307  * Note that successive rsync runs will shuffle the filenames around a
308  * bit as long as the file is still busy; this is because this function
309  * does not know if the unlink call is due to a new file coming in, or
310  * --delete trying to remove old .rsyncNNN files, hence it renames it
311  * each time.
312  **/
313 int robust_unlink(char *fname)
314 {
315 #ifndef ETXTBSY
316         return do_unlink(fname);
317 #else
318         static int counter = 1;
319         int rc, pos, start;
320         char path[MAXPATHLEN];
321
322         rc = do_unlink(fname);
323         if ((rc == 0) || (errno != ETXTBSY))
324                 return rc;
325
326         strlcpy(path, fname, MAXPATHLEN);
327
328         pos = strlen(path);
329         while((path[--pos] != '/') && (pos >= 0))
330                 ;
331         ++pos;
332         strlcpy(&path[pos], ".rsync", MAXPATHLEN-pos);
333         pos += sizeof(".rsync")-1;
334
335         if (pos > (MAXPATHLEN-MAX_RENAMES_DIGITS-1)) {
336                 errno = ETXTBSY;
337                 return -1;
338         }
339
340         /* start where the last one left off to reduce chance of clashes */
341         start = counter;
342         do {
343                 sprintf(&path[pos], "%03d", counter);
344                 if (++counter >= MAX_RENAMES)
345                         counter = 1;
346         } while (((rc = access(path, 0)) == 0) && (counter != start));
347
348         if (verbose > 0)
349                 rprintf(FINFO,"renaming %s to %s because of text busy\n",
350                                             fname, path);
351
352         /* maybe we should return rename()'s exit status? Nah. */
353         if (do_rename(fname, path) != 0) {
354                 errno = ETXTBSY;
355                 return -1;
356         }
357         return 0;
358 #endif
359 }
360
361 int robust_rename(char *from, char *to)
362 {
363 #ifndef ETXTBSY
364         return do_rename(from, to);
365 #else
366         int rc = do_rename(from, to);
367         if ((rc == 0) || (errno != ETXTBSY))
368                 return rc;
369         if (robust_unlink(to) != 0)
370                 return -1;
371         return do_rename(from, to);
372 #endif
373 }
374
375
376 static pid_t all_pids[10];
377 static int num_pids;
378
379 /** Fork and record the pid of the child. **/
380 pid_t do_fork(void)
381 {
382         pid_t newpid = fork();
383         
384         if (newpid != 0  &&  newpid != -1) {
385                 all_pids[num_pids++] = newpid;
386         }
387         return newpid;
388 }
389
390 /**
391  * Kill all children.
392  *
393  * @todo It would be kind of nice to make sure that they are actually
394  * all our children before we kill them, because their pids may have
395  * been recycled by some other process.  Perhaps when we wait for a
396  * child, we should remove it from this array.  Alternatively we could
397  * perhaps use process groups, but I think that would not work on
398  * ancient Unix versions that don't support them.
399  **/
400 void kill_all(int sig)
401 {
402         int i;
403
404         for (i = 0; i < num_pids; i++) {
405                 /* Let's just be a little careful where we
406                  * point that gun, hey?  See kill(2) for the
407                  * magic caused by negative values. */
408                 pid_t p = all_pids[i];
409
410                 if (p == getpid())
411                         continue;
412                 if (p <= 0)
413                         continue;
414
415                 kill(p, sig);
416         }
417 }
418
419
420 /** Turn a user name into a uid */
421 int name_to_uid(char *name, uid_t *uid)
422 {
423         struct passwd *pass;
424         if (!name || !*name) return 0;
425         pass = getpwnam(name);
426         if (pass) {
427                 *uid = pass->pw_uid;
428                 return 1;
429         }
430         return 0;
431 }
432
433 /** Turn a group name into a gid */
434 int name_to_gid(char *name, gid_t *gid)
435 {
436         struct group *grp;
437         if (!name || !*name) return 0;
438         grp = getgrnam(name);
439         if (grp) {
440                 *gid = grp->gr_gid;
441                 return 1;
442         }
443         return 0;
444 }
445
446
447 /** Lock a byte range in a open file */
448 int lock_range(int fd, int offset, int len)
449 {
450         struct flock lock;
451
452         lock.l_type = F_WRLCK;
453         lock.l_whence = SEEK_SET;
454         lock.l_start = offset;
455         lock.l_len = len;
456         lock.l_pid = 0;
457         
458         return fcntl(fd,F_SETLK,&lock) == 0;
459 }
460
461
462 static void glob_expand_one(char *s, char **argv, int *argc, int maxargs)
463 {
464 #if !(defined(HAVE_GLOB) && defined(HAVE_GLOB_H))
465         if (!*s) s = ".";
466         argv[*argc] = strdup(s);
467         (*argc)++;
468         return;
469 #else
470         extern int sanitize_paths;
471         glob_t globbuf;
472         int i;
473
474         if (!*s) s = ".";
475
476         argv[*argc] = strdup(s);
477         if (sanitize_paths) {
478                 sanitize_path(argv[*argc], NULL);
479         }
480
481         memset(&globbuf, 0, sizeof(globbuf));
482         glob(argv[*argc], 0, NULL, &globbuf);
483         if (globbuf.gl_pathc == 0) {
484                 (*argc)++;
485                 globfree(&globbuf);
486                 return;
487         }
488         for (i=0; i<(maxargs - (*argc)) && i < (int) globbuf.gl_pathc;i++) {
489                 if (i == 0) free(argv[*argc]);
490                 argv[(*argc) + i] = strdup(globbuf.gl_pathv[i]);
491                 if (!argv[(*argc) + i]) out_of_memory("glob_expand");
492         }
493         globfree(&globbuf);
494         (*argc) += i;
495 #endif
496 }
497
498 void glob_expand(char *base1, char **argv, int *argc, int maxargs)
499 {
500         char *s = argv[*argc];
501         char *p, *q;
502         char *base = base1;
503
504         if (!s || !*s) return;
505
506         if (strncmp(s, base, strlen(base)) == 0) {
507                 s += strlen(base);
508         }
509
510         s = strdup(s);
511         if (!s) out_of_memory("glob_expand");
512
513         if (asprintf(&base," %s/", base1) <= 0) out_of_memory("glob_expand");
514
515         q = s;
516         while ((p = strstr(q,base)) && ((*argc) < maxargs)) {
517                 /* split it at this point */
518                 *p = 0;
519                 glob_expand_one(q, argv, argc, maxargs);
520                 q = p+strlen(base);
521         }
522
523         if (*q && (*argc < maxargs)) glob_expand_one(q, argv, argc, maxargs);
524
525         free(s);
526         free(base);
527 }
528
529 /**
530  * Convert a string to lower case
531  **/
532 void strlower(char *s)
533 {
534         while (*s) {
535                 if (isupper(*s)) *s = tolower(*s);
536                 s++;
537         }
538 }
539
540 void *Realloc(void *p, int size)
541 {
542         if (!p) return (void *)malloc(size);
543         return (void *)realloc(p, size);
544 }
545
546
547 void clean_fname(char *name)
548 {
549         char *p;
550         int l;
551         int modified = 1;
552
553         if (!name) return;
554
555         while (modified) {
556                 modified = 0;
557
558                 if ((p=strstr(name,"/./"))) {
559                         modified = 1;
560                         while (*p) {
561                                 p[0] = p[2];
562                                 p++;
563                         }
564                 }
565
566                 if ((p=strstr(name,"//"))) {
567                         modified = 1;
568                         while (*p) {
569                                 p[0] = p[1];
570                                 p++;
571                         }
572                 }
573
574                 if (strncmp(p=name,"./",2) == 0) {      
575                         modified = 1;
576                         do {
577                                 p[0] = p[2];
578                         } while (*p++);
579                 }
580
581                 l = strlen(p=name);
582                 if (l > 1 && p[l-1] == '/') {
583                         modified = 1;
584                         p[l-1] = 0;
585                 }
586         }
587 }
588
589 /**
590  * Make path appear as if a chroot had occurred:
591  *
592  * @li 1. remove leading "/" (or replace with "." if at end)
593  *
594  * @li 2. remove leading ".." components (except those allowed by @p reldir)
595  *
596  * @li 3. delete any other "<dir>/.." (recursively)
597  *
598  * Can only shrink paths, so sanitizes in place.
599  *
600  * While we're at it, remove double slashes and "." components like
601  *   clean_fname() does, but DON'T remove a trailing slash because that
602  *   is sometimes significant on command line arguments.
603  *
604  * If @p reldir is non-null, it is a sanitized directory that the path will be
605  *    relative to, so allow as many ".." at the beginning of the path as
606  *    there are components in reldir.  This is used for symbolic link targets.
607  *    If reldir is non-null and the path began with "/", to be completely like
608  *    a chroot we should add in depth levels of ".." at the beginning of the
609  *    path, but that would blow the assumption that the path doesn't grow and
610  *    it is not likely to end up being a valid symlink anyway, so just do
611  *    the normal removal of the leading "/" instead.
612  *
613  * Contributed by Dave Dykstra <dwd@bell-labs.com>
614  */
615 void sanitize_path(char *p, char *reldir)
616 {
617         char *start, *sanp;
618         int depth = 0;
619         int allowdotdot = 0;
620
621         if (reldir) {
622                 depth++;
623                 while (*reldir) {
624                         if (*reldir++ == '/') {
625                                 depth++;
626                         }
627                 }
628         }
629         start = p;
630         sanp = p;
631         while (*p == '/') {
632                 /* remove leading slashes */
633                 p++;
634         }
635         while (*p != '\0') {
636                 /* this loop iterates once per filename component in p.
637                  * both p (and sanp if the original had a slash) should
638                  * always be left pointing after a slash
639                  */
640                 if ((*p == '.') && ((*(p+1) == '/') || (*(p+1) == '\0'))) {
641                         /* skip "." component */
642                         while (*++p == '/') {
643                                 /* skip following slashes */
644                                 ;
645                         }
646                         continue;
647                 }
648                 allowdotdot = 0;
649                 if ((*p == '.') && (*(p+1) == '.') &&
650                             ((*(p+2) == '/') || (*(p+2) == '\0'))) {
651                         /* ".." component followed by slash or end */
652                         if ((depth > 0) && (sanp == start)) {
653                                 /* allow depth levels of .. at the beginning */
654                                 --depth;
655                                 allowdotdot = 1;
656                         } else {
657                                 p += 2;
658                                 if (*p == '/')
659                                         p++;
660                                 if (sanp != start) {
661                                         /* back up sanp one level */
662                                         --sanp; /* now pointing at slash */
663                                         while ((sanp > start) && (*(sanp - 1) != '/')) {
664                                                 /* skip back up to slash */
665                                                 sanp--;
666                                         }
667                                 }
668                                 continue;
669                         }
670                 }
671                 while (1) {
672                         /* copy one component through next slash */
673                         *sanp++ = *p++;
674                         if ((*p == '\0') || (*(p-1) == '/')) {
675                                 while (*p == '/') {
676                                         /* skip multiple slashes */
677                                         p++;
678                                 }
679                                 break;
680                         }
681                 }
682                 if (allowdotdot) {
683                         /* move the virtual beginning to leave the .. alone */
684                         start = sanp;
685                 }
686         }
687         if ((sanp == start) && !allowdotdot) {
688                 /* ended up with nothing, so put in "." component */
689                 /*
690                  * note that the !allowdotdot doesn't prevent this from
691                  *  happening in all allowed ".." situations, but I didn't
692                  *  think it was worth putting in an extra variable to ensure
693                  *  it since an extra "." won't hurt in those situations.
694                  */
695                 *sanp++ = '.';
696         }
697         *sanp = '\0';
698 }
699
700
701 static char curr_dir[MAXPATHLEN];
702
703 /** like chdir() but can be reversed with pop_dir() if save is set. It
704    is also much faster as it remembers where we have been */
705 char *push_dir(char *dir, int save)
706 {
707         char *ret = curr_dir;
708         static int initialised;
709
710         if (!initialised) {
711                 initialised = 1;
712                 getcwd(curr_dir, sizeof(curr_dir)-1);
713         }
714
715         if (!dir) return NULL; /* this call was probably just to initialize */
716
717         if (chdir(dir)) return NULL;
718
719         if (save) {
720                 ret = strdup(curr_dir);
721         }
722
723         if (*dir == '/') {
724                 strlcpy(curr_dir, dir, sizeof(curr_dir));
725         } else {
726                 strlcat(curr_dir,"/", sizeof(curr_dir));
727                 strlcat(curr_dir,dir, sizeof(curr_dir));
728         }
729
730         clean_fname(curr_dir);
731
732         return ret;
733 }
734
735 /** Reverse a push_dir call */
736 int pop_dir(char *dir)
737 {
738         int ret;
739
740         ret = chdir(dir);
741         if (ret) {
742                 free(dir);
743                 return ret;
744         }
745
746         strlcpy(curr_dir, dir, sizeof(curr_dir));
747
748         free(dir);
749
750         return 0;
751 }
752
753 /** We need to supply our own strcmp function for file list comparisons
754    to ensure that signed/unsigned usage is consistent between machines. */
755 int u_strcmp(const char *cs1, const char *cs2)
756 {
757         const uchar *s1 = (const uchar *)cs1;
758         const uchar *s2 = (const uchar *)cs2;
759
760         while (*s1 && *s2 && (*s1 == *s2)) {
761                 s1++; s2++;
762         }
763         
764         return (int)*s1 - (int)*s2;
765 }
766
767
768
769 /**
770  * Determine if a symlink points outside the current directory tree.
771  * This is considered "unsafe" because e.g. when mirroring somebody
772  * else's machine it might allow them to establish a symlink to
773  * /etc/passwd, and then read it through a web server.
774  *
775  * @param dest Target of the symlink in question.
776  *
777  * @param src Top source directory currently applicable.  Basically this
778  * is the first parameter to rsync in a simple invocation, but it's
779  * modified by flist.c in slightly complex ways.
780  *
781  * @retval True if unsafe
782  * @retval False is unsafe
783  **/
784 int unsafe_symlink(char *dest, char *src)
785 {
786         char *tok;
787         int depth = 0;
788
789         /* all absolute and null symlinks are unsafe */
790         if (!dest || !(*dest) || (*dest == '/')) return 1;
791
792         src = strdup(src);
793         if (!src) out_of_memory("unsafe_symlink");
794
795         /* find out what our safety margin is */
796         for (tok=strtok(src,"/"); tok; tok=strtok(NULL,"/")) {
797                 if (strcmp(tok,"..") == 0) {
798                         depth=0;
799                 } else if (strcmp(tok,".") == 0) {
800                         /* nothing */
801                 } else {
802                         depth++;
803                 }
804         }
805         free(src);
806
807         /* drop by one to account for the filename portion */
808         depth--;
809
810         dest = strdup(dest);
811         if (!dest) out_of_memory("unsafe_symlink");
812
813         for (tok=strtok(dest,"/"); tok; tok=strtok(NULL,"/")) {
814                 if (strcmp(tok,"..") == 0) {
815                         depth--;
816                 } else if (strcmp(tok,".") == 0) {
817                         /* nothing */
818                 } else {
819                         depth++;
820                 }
821                 /* if at any point we go outside the current directory then
822                    stop - it is unsafe */
823                 if (depth < 0) break;
824         }
825
826         free(dest);
827         return (depth < 0);
828 }
829
830
831 /**
832  * Return the date and time as a string
833  **/
834 char *timestring(time_t t)
835 {
836         static char TimeBuf[200];
837         struct tm *tm = localtime(&t);
838
839 #ifdef HAVE_STRFTIME
840         strftime(TimeBuf,sizeof(TimeBuf)-1,"%Y/%m/%d %T",tm);
841 #else
842         strlcpy(TimeBuf, asctime(tm), sizeof(TimeBuf));
843 #endif
844
845         if (TimeBuf[strlen(TimeBuf)-1] == '\n') {
846                 TimeBuf[strlen(TimeBuf)-1] = 0;
847         }
848
849         return(TimeBuf);
850 }
851
852
853 /**
854  * Sleep for a specified number of milliseconds.
855  *
856  * Always returns TRUE.  (In the future it might return FALSE if
857  * interrupted.)
858  **/
859 int msleep(int t)
860 {
861         int tdiff=0;
862         struct timeval tval,t1,t2;  
863
864         gettimeofday(&t1, NULL);
865         gettimeofday(&t2, NULL);
866   
867         while (tdiff < t) {
868                 tval.tv_sec = (t-tdiff)/1000;
869                 tval.tv_usec = 1000*((t-tdiff)%1000);
870  
871                 errno = 0;
872                 select(0,NULL,NULL, NULL, &tval);
873
874                 gettimeofday(&t2, NULL);
875                 tdiff = (t2.tv_sec - t1.tv_sec)*1000 + 
876                         (t2.tv_usec - t1.tv_usec)/1000;
877         }
878
879         return True;
880 }
881
882
883 /**
884  * Determine if two file modification times are equivalent (either
885  * exact or in the modification timestamp window established by
886  * --modify-window).
887  *
888  * @retval 0 if the times should be treated as the same
889  *
890  * @retval +1 if the first is later
891  *
892  * @retval -1 if the 2nd is later
893  **/
894 int cmp_modtime(time_t file1, time_t file2)
895 {
896         extern int modify_window;
897
898         if (file2 > file1) {
899                 if (file2 - file1 <= modify_window) return 0;
900                 return -1;
901         }
902         if (file1 - file2 <= modify_window) return 0;
903         return 1;
904 }
905
906
907 #ifdef __INSURE__XX
908 #include <dlfcn.h>
909
910 /**
911    This routine is a trick to immediately catch errors when debugging
912    with insure. A xterm with a gdb is popped up when insure catches
913    a error. It is Linux specific.
914 **/
915 int _Insure_trap_error(int a1, int a2, int a3, int a4, int a5, int a6)
916 {
917         static int (*fn)();
918         int ret;
919         char *cmd;
920
921         asprintf(&cmd, "/usr/X11R6/bin/xterm -display :0 -T Panic -n Panic -e /bin/sh -c 'cat /tmp/ierrs.*.%d ; gdb /proc/%d/exe %d'", 
922                 getpid(), getpid(), getpid());
923
924         if (!fn) {
925                 static void *h;
926                 h = dlopen("/usr/local/parasoft/insure++lite/lib.linux2/libinsure.so", RTLD_LAZY);
927                 fn = dlsym(h, "_Insure_trap_error");
928         }
929
930         ret = fn(a1, a2, a3, a4, a5, a6);
931
932         system(cmd);
933
934         free(cmd);
935
936         return ret;
937 }
938 #endif