/* Randomly return a file * (c) 2001, Luciano Rocha * Released under the GNU GPL * compile: cc -O2 -o randfile randfile.c * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ #include #include #include #include #include #include #include #include #include #include #include /* List of files to return */ typedef struct list { char *name; // name of file struct list *next; // pointer to next in list } list; /* returns a random number r, where 0 <= r < n */ static inline int r(int n) { return (int) ((((float) random()) * ((float) n)) / (float) RAND_MAX); } /* state vars: original working dir, image dir, image dir's name, * symbolic links supported */ int origwd, fileswd, dirnamelen = 0, syml = 0; char *dirname = NULL; /* gets a random list of files in dir */ list *getfiles(const char *dir) { DIR *d; struct dirent *de; int i, nf; list *f, *l, *n; struct stat st; nf = 0; /* number of files already in list */ f = NULL; /* the list of files to return */ if (!(d = opendir("."))) { /* open curr dir (files dir) */ perror(dir); return NULL; } while ((de = readdir(d))) { /* read list of files */ /* skip files starting with . (. .. .zbr) */ if (*(de->d_name) == '.') continue; /* check if is a normal file */ if (stat(de->d_name, &st) || !S_ISREG(st.st_mode)) continue; i = r(++nf); /* get pos of new file in list */ /* in the middle of the list */ if (i) for (l = f; i-- && l->next; l = l->next); /* in the beginning */ else (l = NULL); /* allocate new list member */ if (!(n = (list *) malloc(sizeof(list)))) { perror(de->d_name); break; } /* copy file name into list member */ if (!(n->name = strdup(de->d_name))) { perror(de->d_name); free(n); break; } /* insert list member in middle of list */ if (l) { n->next = l->next; l->next = n; } else { /* insert in beginning of list */ n->next = f; f = n; } } closedir(d); return f; } /* try to copy using just reads & writes, when sendfile(2) fails... */ int old_copy(int fd, int fo) { char *b; int i, l; /* allocate buffer, prefered size is system's page size */ l = getpagesize(); if (l < 512) l = 512; if (!(b = malloc(l))) { perror("malloc(3)"); close(fd); close(fo); return -1; } /* read & write */ while ((i = read(fo, b, l)) > 0 && write(fd, b, i) == i); close(fo); return fd; } /* copy file */ int file_copy(const char *d, const char *o) { int fd, fo; off_t offset; struct stat st; /* change to images directory and open file */ fchdir(fileswd); if ((fo = open(o, O_RDONLY)) < 0) { perror(o); return -1; } /* change to original directory and create destination file */ fchdir(origwd); unlink(d); if ((fd = creat(d, 0644)) < 0) { perror(d); close(fo); exit(1); } offset = 0; /* get length */ if (fstat(fo, &st)) return old_copy(fd, fo); /* try sendfile(2) (faster) */ if (sendfile(fd, fo, &offset, st.st_size) < 0) { perror("sendfile(2)"); return old_copy(fd, fo); } close(fo); return fd; } /* just print the file name */ void do_list(const char *d, const char *o, int _) { printf("%s\n", o); } /* change file after some period of time elapses */ void do_time(const char *d, const char *o, int elapse) { int fd, i; char *file; /* normal file usage, like below */ if (syml || !dirname) fd = file_copy(d, o); else { if (!(file = malloc(dirnamelen + (i = strlen(o)) + 2))) { perror("malloc(3)"); return; } memcpy(file, dirname, dirnamelen); file[dirnamelen] = '/'; memcpy(file + dirnamelen + 1, o, i); file[dirnamelen + 1 + i] = '\0'; fchdir(origwd); unlink(d); if (symlink(file, d)) { syml = 1; fd = file_copy(d, o); } else if ((fd = open(d, O_RDONLY)) < 0) perror(file); free(file); } if (fd < 0) return; close(fd); /* sleep the seconds desired, then just leave for next file */ sleep(elapse); } /* use normal files, but try to use symbolic links if possible */ void do_file(const char *d, const char *o, int _) { int fd, i; struct stat st; char *file; time_t atime; /* symlinks not suported, copy file */ if (syml) fd = file_copy(d, o); else if (!dirname) { /* error getting files directory's absolute name */ syml = 1; /* so: syml not suported (syml to where?) */ fd = file_copy(d, o); /* copy file */ } else { /* construct full file name */ if (!(file = malloc(dirnamelen + (i = strlen(o)) + 2))) { perror("malloc(3)"); return; } memcpy(file, dirname, dirnamelen); file[dirnamelen] = '/'; memcpy(file + dirnamelen + 1, o, i); file[dirnamelen + 1 + i] = '\0'; fchdir(origwd); unlink(d); /* try to create symbolic link */ if (symlink(file, d)) { syml = 1; fd = file_copy(d, o); } else if ((fd = open(d, O_RDONLY)) < 0) perror(file); free(file); } /* error copying file or opening symlink */ if (fd < 0) return; /* get last access time */ if (fstat(fd, &st)) { perror("fstat(2)"); close(fd); return; } atime = st.st_atime; /* wait for some process to read from file */ do { usleep(500000); if (fstat(fd, &st)) { perror("fstat(2)"); close(fd); return; } } while (st.st_atime == atime); close(fd); } /* write file to named pipe/fifo */ void do_pipe(const char *d, const char *o, int _) { int fd, len; void *file; struct stat st; /* open file */ fchdir(fileswd); if ((fd = open(o, O_RDONLY)) < 0) { perror(o); return; } /* get file size */ if (fstat(fd, &st)) { perror("fstat(2)"); close(fd); return; } if ((len = st.st_size) <= 0) { fprintf(stderr, "file %s too small\n", o); close(fd); return; } /* map file to memory */ if ((file = mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED) { perror("mmap(2)"); close(fd); return; } close(fd); /* create fifo */ if (fchdir(origwd)) { perror("fchdir(2)"); exit(1); } unlink(d); if (mkfifo(d, 0644)) { perror(d); exit(1); } /* try to write file to fifo */ if ((fd = open(d, O_WRONLY)) < 0) { perror(d); munmap(file, len); return; } unlink(d); write(fd, file, len); close(fd); } /* just show how to call this little program */ void usage(const char *p) { fprintf(stderr, "usage:\n%s - [] \n" "type can be:\n" " p: named pipe/fifo\n" " f: symbolic link if available, normal file copy otherwise\n" " t