]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/readahead-replay.c
src: our lord is coverity
[thirdparty/systemd.git] / src / readahead-replay.c
CommitLineData
22be093f
LP
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 General Public License as published by
10 the Free Software Foundation; either version 2 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 General Public License for more details.
17
18 You should have received a copy of the GNU 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>
8260358d 35#include <getopt.h>
6624768c 36#include <sys/inotify.h>
22be093f
LP
37
38#include "missing.h"
39#include "util.h"
40#include "set.h"
41#include "sd-daemon.h"
42#include "ioprio.h"
43#include "readahead-common.h"
44
8260358d
LP
45static off_t arg_file_size_max = READAHEAD_FILE_SIZE_MAX;
46
d9c7a87b
LP
47static ReadaheadShared *shared = NULL;
48
22be093f
LP
49static 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
55 assert(pack);
56
57 if (!fgets(fn, sizeof(fn), pack))
58 return 0;
59
60 char_array_0(fn);
61 truncate_nl(fn);
62
a78899f5
LP
63 if ((fd = open(fn, O_RDONLY|O_CLOEXEC|O_NOATIME|O_NOCTTY|O_NOFOLLOW)) < 0) {
64
a76fad09 65 if (errno != ENOENT && errno != EPERM && errno != EACCES)
a78899f5
LP
66 log_warning("open(%s) failed: %m", fn);
67
68 } else if (file_verify(fd, fn, arg_file_size_max, &st) <= 0) {
22be093f
LP
69 close_nointr_nofail(fd);
70 fd = -1;
71 }
72
73 for (;;) {
74 uint32_t b, c;
75
76 if (fread(&b, sizeof(b), 1, pack) != 1 ||
77 fread(&c, sizeof(c), 1, pack) != 1) {
78 log_error("Premature end of pack file.");
79 r = -EIO;
80 goto finish;
81 }
82
83 if (b == 0 && c == 0)
84 break;
85
86 if (c <= b) {
87 log_error("Invalid pack file.");
88 r = -EIO;
89 goto finish;
90 }
91
92 log_debug("%s: page %u to %u", fn, b, c);
93
94 any = true;
95
96 if (fd >= 0)
37f85e66 97 if (posix_fadvise(fd, b * page_size(), (c - b) * page_size(), POSIX_FADV_WILLNEED) < 0) {
6e66797a 98 log_warning("posix_fadvise() failed: %m");
22be093f
LP
99 goto finish;
100 }
101 }
102
103 if (!any && fd >= 0) {
104 /* if no range is encoded in the pack file this is
105 * intended to mean that the whole file shall be
106 * read */
107
6e66797a
HH
108 if (posix_fadvise(fd, 0, st.st_size, POSIX_FADV_WILLNEED) < 0) {
109 log_warning("posix_fadvise() failed: %m");
22be093f
LP
110 goto finish;
111 }
112 }
113
114finish:
115 if (fd >= 0)
116 close_nointr_nofail(fd);
117
118 return r;
119}
120
121static int replay(const char *root) {
858209c5 122 FILE *pack = NULL;
22be093f
LP
123 char line[LINE_MAX];
124 int r = 0;
da19d5c1
LP
125 char *pack_fn = NULL;
126 int c;
902a339c 127 bool on_ssd, ready = false;
22be093f 128 int prio;
6624768c 129 int inotify_fd = -1;
22be093f
LP
130
131 assert(root);
132
75a010e0 133 write_one_line_file("/proc/self/oom_score_adj", "1000");
de58283f 134 bump_request_nr(root);
75a010e0 135
22be093f
LP
136 if (asprintf(&pack_fn, "%s/.readahead", root) < 0) {
137 log_error("Out of memory");
138 r = -ENOMEM;
139 goto finish;
140 }
141
142 if ((!(pack = fopen(pack_fn, "re")))) {
a78899f5 143 if (errno == ENOENT)
22be093f
LP
144 log_debug("No pack file found.");
145 else {
146 log_error("Failed to open pack file: %m");
147 r = -errno;
148 }
149
150 goto finish;
151 }
152
bdb0e14d
LP
153 posix_fadvise(fileno(pack), 0, 0, POSIX_FADV_WILLNEED);
154
6624768c
LP
155 if ((inotify_fd = open_inotify()) < 0) {
156 r = inotify_fd;
157 goto finish;
158 }
159
22be093f
LP
160 if (!(fgets(line, sizeof(line), pack))) {
161 log_error("Premature end of pack file.");
162 r = -EIO;
163 goto finish;
164 }
165
166 char_array_0(line);
167
168 if (!streq(line, CANONICAL_HOST "\n")) {
169 log_debug("Pack file host type mismatch.");
170 goto finish;
171 }
172
173 if ((c = getc(pack)) == EOF) {
174 log_debug("Premature end of pack file.");
175 r = -EIO;
176 goto finish;
177 }
178
179 /* We do not retest SSD here, so that we can start replaying
180 * before udev is up.*/
181 on_ssd = c == 'S';
182 log_debug("On SSD: %s", yes_no(on_ssd));
183
184 if (on_ssd)
185 prio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_IDLE, 0);
186 else
187 prio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_RT, 7);
188
189 if (ioprio_set(IOPRIO_WHO_PROCESS, getpid(), prio) < 0)
190 log_warning("Failed to set IDLE IO priority class: %m");
191
902a339c 192 sd_notify(0, "STATUS=Replaying readahead data");
22be093f
LP
193
194 log_debug("Replaying...");
195
2b583ce6 196 if (access("/run/systemd/readahead/noreplay", F_OK) >= 0) {
6624768c
LP
197 log_debug("Got termination request");
198 goto done;
199 }
200
22be093f 201 while (!feof(pack) && !ferror(pack)) {
6624768c 202 uint8_t inotify_buffer[sizeof(struct inotify_event) + FILENAME_MAX];
22be093f 203 int k;
6624768c
LP
204 ssize_t n;
205
206 if ((n = read(inotify_fd, &inotify_buffer, sizeof(inotify_buffer))) < 0) {
207 if (errno != EINTR && errno != EAGAIN) {
208 log_error("Failed to read inotify event: %m");
209 r = -errno;
210 goto finish;
211 }
212 } else {
213 struct inotify_event *e = (struct inotify_event*) inotify_buffer;
214
215 while (n > 0) {
216 size_t step;
217
218 if ((e->mask & IN_CREATE) && streq(e->name, "noreplay")) {
219 log_debug("Got termination request");
220 goto done;
221 }
222
223 step = sizeof(struct inotify_event) + e->len;
224 assert(step <= (size_t) n);
225
226 e = (struct inotify_event*) ((uint8_t*) e + step);
227 n -= step;
228 }
229 }
22be093f
LP
230
231 if ((k = unpack_file(pack)) < 0) {
232 r = k;
233 goto finish;
234 }
902a339c
LP
235
236 if (!ready) {
237 /* We delay the ready notification until we
238 * queued at least one read */
239 sd_notify(0, "READY=1");
240 ready = true;
241 }
22be093f
LP
242 }
243
6624768c 244done:
902a339c
LP
245 if (!ready)
246 sd_notify(0, "READY=1");
247
22be093f
LP
248 if (ferror(pack)) {
249 log_error("Failed to read pack file.");
250 r = -EIO;
251 goto finish;
252 }
253
254 log_debug("Done.");
255
256finish:
257 if (pack)
258 fclose(pack);
259
6624768c
LP
260 if (inotify_fd >= 0)
261 close_nointr_nofail(inotify_fd);
262
22be093f
LP
263 free(pack_fn);
264
265 return r;
266}
267
8260358d
LP
268
269static int help(void) {
270
271 printf("%s [OPTIONS...] [DIRECTORY]\n\n"
272 "Replay collected read-ahead data on early boot.\n\n"
273 " -h --help Show this help\n"
274 " --max-file-size=BYTES Maximum size of files to read ahead\n",
275 program_invocation_short_name);
276
277 return 0;
278}
279
280static int parse_argv(int argc, char *argv[]) {
281
282 enum {
283 ARG_FILE_SIZE_MAX
284 };
285
286 static const struct option options[] = {
287 { "help", no_argument, NULL, 'h' },
288 { "file-size-max", required_argument, NULL, ARG_FILE_SIZE_MAX },
289 { NULL, 0, NULL, 0 }
290 };
291
292 int c;
293
294 assert(argc >= 0);
295 assert(argv);
296
297 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
298
299 switch (c) {
300
301 case 'h':
302 help();
303 return 0;
304
305 case ARG_FILE_SIZE_MAX: {
306 unsigned long long ull;
307
308 if (safe_atollu(optarg, &ull) < 0 || ull <= 0) {
309 log_error("Failed to parse maximum file size %s.", optarg);
310 return -EINVAL;
311 }
312
313 arg_file_size_max = (off_t) ull;
314 break;
315 }
316
317 case '?':
318 return -EINVAL;
319
320 default:
321 log_error("Unknown option code %c", c);
322 return -EINVAL;
323 }
324 }
325
326 if (optind != argc &&
327 optind != argc-1) {
328 help();
329 return -EINVAL;
330 }
331
332 return 1;
333}
334
22be093f 335int main(int argc, char*argv[]) {
8260358d 336 int r;
2b590e13 337 const char *root;
4030d7a9 338
c1480dae 339 log_set_target(LOG_TARGET_SYSLOG_OR_KMSG);
22be093f
LP
340 log_parse_environment();
341 log_open();
342
8260358d
LP
343 if ((r = parse_argv(argc, argv)) <= 0)
344 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
345
2b590e13
LP
346 root = optind < argc ? argv[optind] : "/";
347
41a598e2
LP
348 if (!enough_ram()) {
349 log_info("Disabling readahead replay due to low memory.");
350 return 0;
351 }
352
07faed4f
LP
353 if (detect_virtualization(NULL) > 0) {
354 log_info("Disabling readahead replay due to execution in virtualized environment.");
46a08e38
LP
355 return 0;
356 }
357
d9c7a87b
LP
358 if (!(shared = shared_get()))
359 return 1;
360
361 shared->replay = getpid();
362 __sync_synchronize();
363
2b590e13 364 if (replay(root) < 0)
22be093f
LP
365 return 1;
366
367 return 0;
368}