chiark / gitweb /
util: replace close_nointr_nofail() by a more useful safe_close()
[elogind.git] / src / readahead / readahead-replay.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2010 Lennart Poettering
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <errno.h>
23 #include <inttypes.h>
24 #include <fcntl.h>
25 #include <linux/limits.h>
26 #include <stdbool.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <sys/select.h>
31 #include <sys/time.h>
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #include <unistd.h>
35 #include <getopt.h>
36 #include <sys/inotify.h>
37
38 #include <systemd/sd-daemon.h>
39
40 #include "missing.h"
41 #include "util.h"
42 #include "set.h"
43 #include "ioprio.h"
44 #include "readahead-common.h"
45 #include "virt.h"
46
47 static ReadaheadShared *shared = NULL;
48
49 static int unpack_file(FILE *pack) {
50         char fn[PATH_MAX];
51         int r = 0, fd = -1;
52         bool any = false;
53         struct stat st;
54         uint64_t inode;
55
56         assert(pack);
57
58         if (!fgets(fn, sizeof(fn), pack))
59                 return 0;
60
61         char_array_0(fn);
62         truncate_nl(fn);
63
64         fd = open(fn, O_RDONLY|O_CLOEXEC|O_NOATIME|O_NOCTTY|O_NOFOLLOW);
65         if (fd < 0) {
66
67                 if (errno != ENOENT && errno != EPERM && errno != EACCES && errno != ELOOP)
68                         log_warning("open(%s) failed: %m", fn);
69
70         } else if (file_verify(fd, fn, arg_file_size_max, &st) <= 0)
71                 fd = safe_close(fd);
72
73         if (fread(&inode, sizeof(inode), 1, pack) != 1) {
74                 log_error("Premature end of pack file.");
75                 r = -EIO;
76                 goto finish;
77         }
78
79         if (fd >= 0) {
80                 /* If the inode changed the file got deleted, so just
81                  * ignore this entry */
82                 if (st.st_ino != (uint64_t) inode)
83                         fd = safe_close(fd);
84         }
85
86         for (;;) {
87                 uint32_t b, c;
88
89                 if (fread(&b, sizeof(b), 1, pack) != 1 ||
90                     fread(&c, sizeof(c), 1, pack) != 1) {
91                         log_error("Premature end of pack file.");
92                         r = -EIO;
93                         goto finish;
94                 }
95
96                 if (b == 0 && c == 0)
97                         break;
98
99                 if (c <= b) {
100                         log_error("Invalid pack file.");
101                         r = -EIO;
102                         goto finish;
103                 }
104
105                 log_debug("%s: page %u to %u", fn, b, c);
106
107                 any = true;
108
109                 if (fd >= 0)
110                         if (posix_fadvise(fd, b * page_size(), (c - b) * page_size(), POSIX_FADV_WILLNEED) < 0) {
111                                 log_warning("posix_fadvise() failed: %m");
112                                 goto finish;
113                         }
114         }
115
116         if (!any && fd >= 0) {
117                 /* if no range is encoded in the pack file this is
118                  * intended to mean that the whole file shall be
119                  * read */
120
121                 if (posix_fadvise(fd, 0, st.st_size, POSIX_FADV_WILLNEED) < 0) {
122                         log_warning("posix_fadvise() failed: %m");
123                         goto finish;
124                 }
125         }
126
127 finish:
128         safe_close(fd);
129
130         return r;
131 }
132
133 static int replay(const char *root) {
134         FILE *pack = NULL;
135         char line[LINE_MAX];
136         int r = 0;
137         char *pack_fn = NULL;
138         int c;
139         bool on_ssd, ready = false;
140         int prio;
141         int inotify_fd = -1;
142
143         assert(root);
144
145         block_bump_request_nr(root);
146
147         if (asprintf(&pack_fn, "%s/.readahead", root) < 0) {
148                 r = log_oom();
149                 goto finish;
150         }
151
152         pack = fopen(pack_fn, "re");
153         if (!pack) {
154                 if (errno == ENOENT)
155                         log_debug("No pack file found.");
156                 else {
157                         log_error("Failed to open pack file: %m");
158                         r = -errno;
159                 }
160
161                 goto finish;
162         }
163
164         posix_fadvise(fileno(pack), 0, 0, POSIX_FADV_WILLNEED);
165
166         if ((inotify_fd = open_inotify()) < 0) {
167                 r = inotify_fd;
168                 goto finish;
169         }
170
171         if (!(fgets(line, sizeof(line), pack))) {
172                 log_error("Premature end of pack file.");
173                 r = -EIO;
174                 goto finish;
175         }
176
177         char_array_0(line);
178
179         if (!streq(line, CANONICAL_HOST READAHEAD_PACK_FILE_VERSION)) {
180                 log_debug("Pack file host or version type mismatch.");
181                 goto finish;
182         }
183
184         if ((c = getc(pack)) == EOF) {
185                 log_debug("Premature end of pack file.");
186                 r = -EIO;
187                 goto finish;
188         }
189
190         /* We do not retest SSD here, so that we can start replaying
191          * before udev is up.*/
192         on_ssd = c == 'S';
193         log_debug("On SSD: %s", yes_no(on_ssd));
194
195         if (on_ssd)
196                 prio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_IDLE, 0);
197         else
198                 /* We are not using RT here, since we'd starve IO that
199                 we didn't record (which is for example blkid, since
200                 its disk accesses go directly to the block device and
201                 are thus not visible in fallocate) to death. However,
202                 we do ask for an IO prio that is slightly higher than
203                 the default (which is BE. 4) */
204                 prio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, 2);
205
206         if (ioprio_set(IOPRIO_WHO_PROCESS, getpid(), prio) < 0)
207                 log_warning("Failed to set IDLE IO priority class: %m");
208
209         sd_notify(0, "STATUS=Replaying readahead data");
210
211         log_debug("Replaying...");
212
213         if (access("/run/systemd/readahead/noreplay", F_OK) >= 0) {
214                 log_debug("Got termination request");
215                 goto done;
216         }
217
218         while (!feof(pack) && !ferror(pack)) {
219                 uint8_t inotify_buffer[sizeof(struct inotify_event) + FILENAME_MAX];
220                 int k;
221                 ssize_t n;
222
223                 if ((n = read(inotify_fd, &inotify_buffer, sizeof(inotify_buffer))) < 0) {
224                         if (errno != EINTR && errno != EAGAIN) {
225                                 log_error("Failed to read inotify event: %m");
226                                 r = -errno;
227                                 goto finish;
228                         }
229                 } else {
230                         struct inotify_event *e = (struct inotify_event*) inotify_buffer;
231
232                         while (n > 0) {
233                                 size_t step;
234
235                                 if ((e->mask & IN_CREATE) && streq(e->name, "noreplay")) {
236                                         log_debug("Got termination request");
237                                         goto done;
238                                 }
239
240                                 step = sizeof(struct inotify_event) + e->len;
241                                 assert(step <= (size_t) n);
242
243                                 e = (struct inotify_event*) ((uint8_t*) e + step);
244                                 n -= step;
245                         }
246                 }
247
248                 if ((k = unpack_file(pack)) < 0) {
249                         r = k;
250                         goto finish;
251                 }
252
253                 if (!ready) {
254                         /* We delay the ready notification until we
255                          * queued at least one read */
256                         sd_notify(0, "READY=1");
257                         ready = true;
258                 }
259         }
260
261 done:
262         if (!ready)
263                 sd_notify(0, "READY=1");
264
265         if (ferror(pack)) {
266                 log_error("Failed to read pack file.");
267                 r = -EIO;
268                 goto finish;
269         }
270
271         log_debug("Done.");
272
273 finish:
274         if (pack)
275                 fclose(pack);
276
277         safe_close(inotify_fd);
278
279         free(pack_fn);
280
281         return r;
282 }
283
284 int main_replay(const char *root) {
285
286         if (!root)
287                 root = "/";
288
289         if (!enough_ram()) {
290                 log_info("Disabling readahead replay due to low memory.");
291                 return EXIT_SUCCESS;
292         }
293
294         shared = shared_get();
295         if (!shared)
296                 return EXIT_FAILURE;
297
298         shared->replay = getpid();
299         __sync_synchronize();
300
301         if (replay(root) < 0)
302                 return EXIT_FAILURE;
303
304         return EXIT_SUCCESS;
305 }