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