]> sourceware.org Git - systemtap.git/blob - cache.cxx
Merge branch 'master' of git://sources.redhat.com/git/systemtap
[systemtap.git] / cache.cxx
1 // systemtap cache manager
2 // Copyright (C) 2006-2009 Red Hat Inc.
3 //
4 // This file is part of systemtap, and is free software. You can
5 // redistribute it and/or modify it under the terms of the GNU General
6 // Public License (GPL); either version 2, or (at your option) any
7 // later version.
8
9 #include "config.h"
10 #include "session.h"
11 #include "cache.h"
12 #include "util.h"
13 #include <cerrno>
14 #include <string>
15 #include <fstream>
16 #include <cstring>
17
18 extern "C" {
19 #include <sys/types.h>
20 #include <sys/stat.h>
21 #include <fcntl.h>
22 #include <glob.h>
23 }
24
25 using namespace std;
26
27
28 #define SYSTEMTAP_CACHE_MAX_FILENAME "cache_mb_limit"
29 #define SYSTEMTAP_CACHE_DEFAULT_MB 64
30
31 struct cache_ent_info {
32 string path;
33 bool is_module;
34 size_t size;
35 long weight; //lower == removed earlier
36
37 cache_ent_info(const string& path, bool is_module);
38 bool operator<(const struct cache_ent_info& other) const
39 { return weight < other.weight; }
40 void unlink() const;
41 };
42
43 static void clean_cache(systemtap_session& s);
44
45
46 void
47 add_to_cache(systemtap_session& s)
48 {
49 string stapconf_src_path = s.tmpdir + "/" + s.stapconf_name;
50 if (s.verbose > 1)
51 clog << "Copying " << stapconf_src_path << " to " << s.stapconf_path << endl;
52 if (copy_file(stapconf_src_path.c_str(), s.stapconf_path.c_str()) != 0)
53 {
54 cerr << "Copy failed (\"" << stapconf_src_path << "\" to \""
55 << s.stapconf_path << "\"): " << strerror(errno) << endl;
56 s.use_cache = false;
57 return;
58 }
59
60 string module_src_path = s.tmpdir + "/" + s.module_name + ".ko";
61 if (s.verbose > 1)
62 clog << "Copying " << module_src_path << " to " << s.hash_path << endl;
63 if (copy_file(module_src_path.c_str(), s.hash_path.c_str()) != 0)
64 {
65 cerr << "Copy failed (\"" << module_src_path << "\" to \""
66 << s.hash_path << "\"): " << strerror(errno) << endl;
67 s.use_cache = false;
68 return;
69 }
70
71 #if HAVE_NSS
72 // This is the name of the cached module signature.
73 string module_signature_dest_path = s.hash_path;
74 module_signature_dest_path += ".sgn";
75
76 if (!s.cert_db_path.empty())
77 {
78 // Copy the module signature, if it was signed.
79 string module_signature_src_path = module_src_path;
80 module_signature_src_path += ".sgn";
81
82 if (s.verbose > 1)
83 clog << "Copying " << module_signature_src_path << " to " << module_signature_dest_path << endl;
84 if (copy_file(module_signature_src_path.c_str(), module_signature_dest_path.c_str()) != 0)
85 {
86 cerr << "Copy failed (\"" << module_signature_src_path << "\" to \""
87 << module_signature_dest_path << "\"): " << strerror(errno) << endl;
88 // NB: this is not so severe as to prevent reuse of the .ko
89 // already copied.
90 //
91 // s.use_cache = false;
92 }
93 }
94 else
95 {
96 // If this module was not signed, then delete any existing signature from the cache.
97 // This is not a fatal error. Even if the existing signature happens to match a
98 // new module later, it still means that the module is identical to one generated by a
99 // trusted server.
100 if (remove_file_or_dir (module_signature_dest_path.c_str()) != 0)
101 cerr << "Failed to remove \"" << module_signature_dest_path << "\" from the cache: "
102 << strerror(errno) << endl;
103 }
104 #endif /* HAVE_NSS */
105
106 string c_dest_path = s.hash_path;
107 if (c_dest_path.rfind(".ko") == (c_dest_path.size() - 3))
108 c_dest_path.resize(c_dest_path.size() - 3);
109 c_dest_path += ".c";
110
111 if (s.verbose > 1)
112 clog << "Copying " << s.translated_source << " to " << c_dest_path
113 << endl;
114 if (copy_file(s.translated_source.c_str(), c_dest_path.c_str()) != 0)
115 {
116 if (s.verbose > 1)
117 cerr << "Copy failed (\"" << s.translated_source << "\" to \""
118 << c_dest_path << "\"): " << strerror(errno) << endl;
119 // NB: this is not so severe as to prevent reuse of the .ko
120 // already copied.
121 //
122 // s.use_cache = false;
123 }
124
125 clean_cache(s);
126 }
127
128
129 bool
130 get_from_cache(systemtap_session& s)
131 {
132 string stapconf_dest_path = s.tmpdir + "/" + s.stapconf_name;
133 string module_dest_path = s.tmpdir + "/" + s.module_name + ".ko";
134 string c_src_path = s.hash_path;
135 int fd_stapconf, fd_module, fd_c;
136 #if HAVE_NSS
137 string hash_signature_path = s.hash_path + ".sgn";
138 int fd_signature;
139 #endif
140
141 if (c_src_path.rfind(".ko") == (c_src_path.size() - 3))
142 c_src_path.resize(c_src_path.size() - 3);
143 c_src_path += ".c";
144
145 // See if stapconf exists
146 fd_stapconf = open(s.stapconf_path.c_str(), O_RDONLY);
147 if (fd_stapconf == -1)
148 {
149 // It isn't in cache.
150 return false;
151 }
152
153 // Copy the stapconf header file to the destination
154 if (copy_file(s.stapconf_path.c_str(), stapconf_dest_path.c_str()) != 0)
155 {
156 cerr << "Copy failed (\"" << s.stapconf_path << "\" to \""
157 << stapconf_dest_path << "\"): " << strerror(errno) << endl;
158 close(fd_stapconf);
159 return false;
160 }
161
162 // We're done with this file handle.
163 close(fd_stapconf);
164
165 if (s.verbose > 1)
166 clog << "Pass 3: using cached " << s.stapconf_path << endl;
167
168 // See if module exists
169 fd_module = open(s.hash_path.c_str(), O_RDONLY);
170 if (fd_module == -1)
171 {
172 // It isn't in cache.
173 return false;
174 }
175
176 // See if C file exists.
177 fd_c = open(c_src_path.c_str(), O_RDONLY);
178 if (fd_c == -1)
179 {
180 // The module is there, but the C file isn't. Cleanup and
181 // return.
182 close(fd_module);
183 unlink(s.hash_path.c_str());
184 return false;
185 }
186
187 // Copy the cached C file to the destination
188 if (copy_file(c_src_path.c_str(), s.translated_source.c_str()) != 0)
189 {
190 cerr << "Copy failed (\"" << c_src_path << "\" to \""
191 << s.translated_source << "\"): " << strerror(errno) << endl;
192 close(fd_module);
193 close(fd_c);
194 return false;
195 }
196
197 // Copy the cached module to the destination (if needed)
198 if (s.last_pass != 3)
199 {
200 if (copy_file(s.hash_path.c_str(), module_dest_path.c_str()) != 0)
201 {
202 cerr << "Copy failed (\"" << s.hash_path << "\" to \""
203 << module_dest_path << "\"): " << strerror(errno) << endl;
204 unlink(c_src_path.c_str());
205 close(fd_module);
206 close(fd_c);
207 return false;
208 }
209 #if HAVE_NSS
210 // See if module signature exists. It's not an error if it doesn't. It just
211 // means that the module is unsigned.
212 fd_signature = open(hash_signature_path.c_str(), O_RDONLY);
213 if (fd_signature != -1) {
214 string signature_dest_path = module_dest_path + ".sgn";
215 close(fd_signature);
216 if (copy_file(hash_signature_path.c_str(), signature_dest_path.c_str()) != 0)
217 {
218 cerr << "Copy failed (\"" << hash_signature_path << "\" to \""
219 << signature_dest_path << "\"): " << strerror(errno) << endl;
220 unlink(c_src_path.c_str());
221 close(fd_module);
222 close(fd_c);
223 return false;
224 }
225 }
226 #endif
227 }
228
229 // We're done with these file handles.
230 close(fd_module);
231 close(fd_c);
232
233 // To preserve semantics (since this will happen if we're not
234 // caching), display the C source if the last pass is 3.
235 if (s.last_pass == 3)
236 {
237 ifstream i (s.translated_source.c_str());
238 cout << i.rdbuf();
239 }
240 // And similarly, display probe module name for -p4.
241 if (s.last_pass == 4)
242 cout << s.hash_path << endl;
243
244 // If everything worked, tell the user. We need to do this here,
245 // since if copying the cached C file works, but copying the cached
246 // module fails, we remove the cached C file and let the C file get
247 // regenerated.
248 if (s.verbose)
249 {
250 clog << "Pass 3: using cached " << c_src_path << endl;
251 if (s.last_pass != 3)
252 clog << "Pass 4: using cached " << s.hash_path << endl;
253 }
254
255 return true;
256 }
257
258
259 static void
260 clean_cache(systemtap_session& s)
261 {
262 if (s.cache_path != "")
263 {
264 /* Get cache size limit from file in the stap cache dir */
265 string cache_max_filename = s.cache_path + "/";
266 cache_max_filename += SYSTEMTAP_CACHE_MAX_FILENAME;
267 ifstream cache_max_file(cache_max_filename.c_str(), ios::in);
268 unsigned long cache_mb_max;
269
270 if (cache_max_file.is_open())
271 {
272 cache_max_file >> cache_mb_max;
273 cache_max_file.close();
274 }
275 else
276 {
277 //file doesnt exist, create a default size
278 ofstream default_cache_max(cache_max_filename.c_str(), ios::out);
279 default_cache_max << SYSTEMTAP_CACHE_DEFAULT_MB << endl;
280 cache_mb_max = SYSTEMTAP_CACHE_DEFAULT_MB;
281
282 if (s.verbose > 1)
283 clog << "Cache limit file " << s.cache_path << "/"
284 << SYSTEMTAP_CACHE_MAX_FILENAME
285 << " missing, creating default." << endl;
286 }
287
288 //glob for all kernel modules in the cache dir
289 glob_t cache_glob;
290 string glob_str = s.cache_path + "/*/*.ko";
291 glob(glob_str.c_str(), 0, NULL, &cache_glob);
292
293
294 set<struct cache_ent_info> cache_contents;
295 unsigned long cache_size_b = 0;
296
297 //grab info for each cache entry (.ko and .c)
298 for (unsigned int i = 0; i < cache_glob.gl_pathc; i++)
299 {
300 string cache_ent_path = cache_glob.gl_pathv[i];
301 cache_ent_path.resize(cache_ent_path.length() - 3);
302
303 struct cache_ent_info cur_info(cache_ent_path, true);
304 if (cur_info.size != 0 && cur_info.weight != 0)
305 {
306 cache_size_b += cur_info.size;
307 cache_contents.insert(cur_info);
308 }
309 }
310
311 globfree(&cache_glob);
312
313 //grab info for each typequery user module (.so)
314 glob_str = s.cache_path + "/*/*.so";
315 glob(glob_str.c_str(), 0, NULL, &cache_glob);
316 for (unsigned int i = 0; i < cache_glob.gl_pathc; i++)
317 {
318 string cache_ent_path = cache_glob.gl_pathv[i];
319 struct cache_ent_info cur_info(cache_ent_path, false);
320 if (cur_info.size != 0 && cur_info.weight != 0)
321 {
322 cache_size_b += cur_info.size;
323 cache_contents.insert(cur_info);
324 }
325 }
326
327 globfree(&cache_glob);
328
329 //grab info for each stapconf cache entry (.h)
330 glob_str = s.cache_path + "/*/*.h";
331 glob(glob_str.c_str(), 0, NULL, &cache_glob);
332 for (unsigned int i = 0; i < cache_glob.gl_pathc; i++)
333 {
334 string cache_ent_path = cache_glob.gl_pathv[i];
335 struct cache_ent_info cur_info(cache_ent_path, false);
336 if (cur_info.size != 0 && cur_info.weight != 0)
337 {
338 cache_size_b += cur_info.size;
339 cache_contents.insert(cur_info);
340 }
341 }
342
343 globfree(&cache_glob);
344
345 set<struct cache_ent_info>::iterator i;
346 unsigned long r_cache_size = cache_size_b;
347 string removed_dirs = "";
348
349 //unlink .ko and .c until the cache size is under the limit
350 for (i = cache_contents.begin(); i != cache_contents.end(); ++i)
351 {
352 if ( (r_cache_size / 1024 / 1024) < cache_mb_max) //convert r_cache_size to MiB
353 break;
354
355 //remove this (*i) cache_entry, add to removed list
356 i->unlink();
357 r_cache_size -= i->size;
358 removed_dirs += i->path + ", ";
359 }
360
361 cache_contents.clear();
362
363 if (s.verbose > 1 && removed_dirs != "")
364 {
365 //remove trailing ", "
366 removed_dirs = removed_dirs.substr(0, removed_dirs.length() - 2);
367 clog << "Cache cleaning successful, removed entries: "
368 << removed_dirs << endl;
369 }
370 }
371 else
372 {
373 if (s.verbose > 1)
374 clog << "Cache cleaning skipped, no cache path." << endl;
375 }
376 }
377
378 // Get the size of a file in bytes
379 static size_t
380 get_file_size(const string &path)
381 {
382 struct stat file_info;
383
384 if (stat(path.c_str(), &file_info) == 0)
385 return file_info.st_size;
386 else
387 return 0;
388 }
389
390 //Assign a weight for a particular file. A lower weight
391 // will be removed before a higher weight.
392 //TODO: for now use system mtime... later base a
393 // weighting on size, ctime, atime etc..
394 static long
395 get_file_weight(const string &path)
396 {
397 time_t dir_mtime = 0;
398 struct stat dir_stat_info;
399
400 if (stat(path.c_str(), &dir_stat_info) == 0)
401 //GNU struct stat defines st_atime as st_atim.tv_sec
402 // but it doesnt seem to work properly in practice
403 // so use st_atim.tv_sec -- bad for portability?
404 dir_mtime = dir_stat_info.st_mtim.tv_sec;
405
406 return dir_mtime;
407 }
408
409
410 cache_ent_info::cache_ent_info(const string& path, bool is_module):
411 path(path), is_module(is_module)
412 {
413 if (is_module)
414 {
415 string mod_path = path + ".ko";
416 string source_path = path + ".c";
417 size = get_file_size(mod_path) + get_file_size(source_path);
418 weight = get_file_weight(mod_path);
419 }
420 else
421 {
422 size = get_file_size(path);
423 weight = get_file_weight(path);
424 }
425 }
426
427
428 void
429 cache_ent_info::unlink() const
430 {
431 if (is_module)
432 {
433 string mod_path = path + ".ko";
434 string source_path = path + ".c";
435 ::unlink(mod_path.c_str());
436 ::unlink(source_path.c_str());
437 }
438 else
439 ::unlink(path.c_str());
440 }
441
442 /* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */
This page took 0.056701 seconds and 5 git commands to generate.