1 /* $Id: mlockfile.c 10018 2016-05-05 12:45:13Z iulius $ */
2 
3 /* Locks the files given on the command line into memory using mlock.
4    This code has only been tested on Solaris and may not work on other
5    platforms.
6 
7    Contributed by Alex Kiernan <alexk@demon.net>.  */
8 
9 #include <sys/types.h>
10 #include <errno.h>
11 #include <fcntl.h>
12 #include <poll.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <sysexits.h>
17 #include <unistd.h>
18 #include <sys/mman.h>
19 #include <sys/stat.h>
20 
21 struct mlock {
22     const char *path;
23     struct stat st;
24     void *base;
25     off_t offset;
26     size_t length;
27 };
28 
29 void inn_lock_files(struct mlock *);
30 
31 char *progname;
32 
33 int flush = 0;
34 int interval = 60000;
35 
36 void
inn_lock_files(struct mlock * ml)37 inn_lock_files(struct mlock *ml)
38 {
39     for (; ml->path != NULL; ++ml) {
40 	int fd;
41 
42 	fd = open(ml->path, O_RDONLY);
43 	if (fd == -1) {
44 	    fprintf(stderr, "%s: can't open `%s' - %s\n",
45 		    progname, ml->path, strerror(errno));
46 	} else {
47 	    struct stat st;
48 
49 	    /* check if size, inode or device of the path have
50 	     * changed, if so unlock the previous file & lock the new
51 	     * one */
52 	    if (fstat(fd, &st) != 0) {
53 		fprintf(stderr, "%s: can't stat `%s' - %s\n",
54 			progname, ml->path, strerror(errno));
55 	    } else if (ml->st.st_ino != st.st_ino ||
56 		     ml->st.st_dev != st.st_dev ||
57 		     ml->st.st_size != st.st_size) {
58 		if (ml->base != MAP_FAILED)
59 		    munmap(ml->base,
60 			   ml->length > 0 ? ml->length : (size_t) ml->st.st_size);
61 
62 		/* free everything here, so in case of failure we try
63 		 * again next time */
64 		ml->st.st_ino = 0;
65 		ml->st.st_dev = 0;
66 		ml->st.st_size = 0;
67 
68 		ml->base = mmap(NULL,
69 				ml->length > 0 ? ml->length : (size_t) st.st_size,
70 				PROT_READ,
71 				MAP_SHARED, fd, ml->offset);
72 
73 		if (ml->base == MAP_FAILED) {
74 		    fprintf(stderr, "%s: can't mmap `%s' - %s\n",
75 			    progname, ml->path, strerror(errno));
76 		} else {
77 		    if (mlock(ml->base,
78 			      ml->length > 0 ? ml->length : (size_t) st.st_size) != 0) {
79 			fprintf(stderr, "%s: can't mlock `%s' - %s\n",
80 				progname, ml->path, strerror(errno));
81 		    } else {
82 			ml->st = st;
83 		    }
84 		}
85 	    } else if (flush) {
86 		msync(ml->base, ml->length > 0 ? ml->length : (size_t) st.st_size, MS_SYNC);
87 	    }
88 	}
89 	close (fd);
90     }
91 }
92 
93 static void
usage(void)94 usage(void)
95 {
96     fprintf(stderr,
97 	    "usage: %s [-f] [-i interval] file[@offset[:length]] ...\n",
98 	    progname);
99     fprintf(stderr, "    -f\tflush locked bitmaps at interval\n");
100     fprintf(stderr, "    -i interval\n\tset interval between checks/flushes\n");
101 }
102 
103 int
main(int argc,char * argv[])104 main(int argc, char *argv[])
105 {
106     struct mlock *ml;
107     int i;
108 
109     progname = *argv;
110     while ((i = getopt(argc, argv, "fi:")) != EOF) {
111 	switch (i) {
112 	case 'i':
113 	    interval = 1000 * atoi(optarg);
114 	    break;
115 
116 	case 'f':
117 	    flush = 1;
118 	    break;
119 
120 	default:
121 	    usage();
122 	    return EX_USAGE;
123 	}
124     }
125     argc -= optind;
126     argv += optind;
127 
128     /* construct list of pathnames which we're to operate on, zero out
129      * the "cookies" so we lock it in core first time through */
130     ml = malloc((1 + argc) * sizeof ml);
131     for (i = 0; argc--; ++i, ++argv) {
132 	char *at;
133 	off_t offset = 0;
134 	size_t length = 0;
135 
136 	ml[i].path = *argv;
137 	ml[i].st.st_ino = 0;
138 	ml[i].st.st_dev = 0;
139 	ml[i].st.st_size = 0;
140 	ml[i].base = MAP_FAILED;
141 
142 	/* if we have a filename of the form ...@offset:length, only
143 	 * map in that portion of the file */
144 	at = strchr(*argv, '@');
145 	if (at != NULL) {
146 	    char *end;
147 
148 	    *at++ = '\0';
149 	    errno = 0;
150 	    offset = strtoull(at, &end, 0);
151 	    if (errno != 0) {
152 		fprintf(stderr, "%s: can't parse offset `%s' - %s\n",
153 			progname, at, strerror(errno));
154                 free(ml);
155 		return EX_USAGE;
156 	    }
157 	    if (*end == ':') {
158 		at = end + 1;
159 		errno = 0;
160 		length = strtoul(at, &end, 0);
161 		if (errno != 0) {
162 		    fprintf(stderr, "%s: can't parse length `%s' - %s\n",
163 			    progname, at, strerror(errno));
164                     free(ml);
165 		    return EX_USAGE;
166 		}
167 	    }
168 	    if (*end != '\0') {
169 		fprintf(stderr, "%s: unrecognised separator `%c'\n",
170 			progname, *end);
171                 free(ml);
172 		return EX_USAGE;
173 	    }
174 	}
175 	ml[i].offset = offset;
176 	ml[i].length = length;
177     }
178     ml[i].path = NULL;
179 
180     /* loop over the list of paths, sleeping 60s between iterations */
181     for (;;) {
182 	inn_lock_files(ml);
183 	poll(NULL, 0, interval);
184     }
185     free(ml);
186     return EX_OSERR;
187 }
188