]> sourceware.org Git - systemtap.git/blame - csclient.cxx
Add guard for unsupported sdt architectures.
[systemtap.git] / csclient.cxx
CommitLineData
2dce8c42
DB
1/*
2 Compile server client functions
3 Copyright (C) 2010 Red Hat Inc.
4
5 This file is part of systemtap, and is free software. You can
6 redistribute it and/or modify it under the terms of the GNU General
7 Public License (GPL); either version 2, or (at your option) any
8 later version.
9*/
10#include "config.h"
11#include "session.h"
aa4d21c0 12#include "csclient.h"
2fad97fd 13#include "util.h"
aa4d21c0 14#include "sys/sdt.h"
2dce8c42 15
aa4d21c0 16#include <sys/times.h>
2dce8c42 17#include <vector>
2fad97fd
DB
18#include <fstream>
19#include <sstream>
79efe7f8 20#include <cassert>
b0e5fa85
DB
21#include <cstdlib>
22#include <cstdio>
23#include <iomanip>
24#include <algorithm>
79efe7f8
FCE
25
26extern "C" {
004a4482 27#include <linux/limits.h>
3eee0879 28#include <sys/time.h>
79efe7f8 29#include <glob.h>
1f9938b9 30#include <limits.h>
f88498d9 31#include <sys/socket.h>
1a7f7b3f 32#include <sys/stat.h>
f88498d9
DS
33#include <netdb.h>
34#include <arpa/inet.h>
0d1534e8 35#include <pwd.h>
79efe7f8 36}
2dce8c42
DB
37
38#if HAVE_AVAHI
39extern "C" {
2dce8c42
DB
40#include <avahi-client/client.h>
41#include <avahi-client/lookup.h>
42
43#include <avahi-common/simple-watch.h>
44#include <avahi-common/malloc.h>
45#include <avahi-common/error.h>
46#include <avahi-common/timeval.h>
3eee0879 47}
b0e5fa85 48#endif // HAVE_AVAHI
2fad97fd
DB
49
50#if HAVE_NSS
3eee0879 51extern "C" {
2fad97fd
DB
52#include <ssl.h>
53#include <nspr.h>
54#include <nss.h>
c77af0d0 55#include <certdb.h>
2fad97fd
DB
56#include <pk11pub.h>
57#include <prerror.h>
58#include <secerr.h>
59#include <sslerr.h>
60
61#include "nsscommon.h"
2dce8c42 62}
c77af0d0 63#endif // HAVE_NSS
2dce8c42 64
aa4d21c0 65using namespace std;
2dce8c42 66
b0e5fa85
DB
67// Information about compile servers.
68struct compile_server_info
69{
70 compile_server_info () : port (0) {}
71
72 std::string host_name;
73 std::string ip_address;
74 unsigned short port;
75 std::string sysinfo;
76 std::string certinfo;
77
78 bool operator== (const compile_server_info &that) const
79 {
80 // The host name must always match.
81 if (this->host_name != that.host_name)
82 return false;
83 // Compare the other fields only if they have both been set.
84 if (this->port != 0 && that.port != 0 && this->port != that.port)
85 return false;
86 if (! this->sysinfo.empty () && ! that.sysinfo.empty () &&
87 this->sysinfo != that.sysinfo)
88 return false;
89 if (! this->certinfo.empty () && ! that.certinfo.empty () &&
90 this->certinfo != that.certinfo)
91 return false;
92 return true;
93 }
94};
95
3d9dfde6
DB
96ostream &operator<< (ostream &s, const compile_server_info &i);
97
b0e5fa85
DB
98// For filtering queries.
99enum compile_server_properties {
100 compile_server_all = 0x1,
101 compile_server_trusted = 0x2,
102 compile_server_online = 0x4,
103 compile_server_compatible = 0x8,
104 compile_server_signer = 0x10,
105 compile_server_specified = 0x20
106};
107
108// Static functions.
109static void query_server_status (systemtap_session &s, const string &status_string);
b0e5fa85
DB
110
111static void get_server_info (systemtap_session &s, int pmask, vector<compile_server_info> &servers);
112static void get_all_server_info (systemtap_session &s, vector<compile_server_info> &servers);
113static void get_default_server_info (systemtap_session &s, vector<compile_server_info> &servers);
1a7f7b3f 114static void get_specified_server_info (systemtap_session &s, vector<compile_server_info> &servers, bool no_default = false);
b0e5fa85
DB
115static void get_or_keep_online_server_info (systemtap_session &s, vector<compile_server_info> &servers);
116static void get_or_keep_trusted_server_info (systemtap_session &s, vector<compile_server_info> &servers);
117static void get_or_keep_signing_server_info (systemtap_session &s, vector<compile_server_info> &servers);
118static void keep_compatible_server_info (systemtap_session &s, vector<compile_server_info> &servers);
119static void keep_common_server_info (const vector<compile_server_info> &info_to_keep, vector<compile_server_info> &filtered_info);
b0e5fa85
DB
120static void add_server_info (const compile_server_info &info, vector<compile_server_info>& list);
121static void merge_server_info (const compile_server_info &source, compile_server_info &target);
122static void merge_server_info (const vector<compile_server_info> &source, vector<compile_server_info> &target);
123static void merge_server_info (const vector<compile_server_info> &source, compile_server_info &target);
124
125static void resolve_server (systemtap_session& s, compile_server_info &server_info);
126static int resolve_host_name (systemtap_session& s, string &host_name, string *ip_address = NULL);
127
c77af0d0 128#if HAVE_NSS
b0e5fa85
DB
129static const char *server_cert_nickname = "stap-server";
130
3d9dfde6
DB
131static void add_server_trust (systemtap_session &s, const string &cert_db_path, const vector<compile_server_info> &server_list);
132static void revoke_server_trust (systemtap_session &s, const string &cert_db_path, const vector<compile_server_info> &server_list);
133static void get_server_info_from_db (systemtap_session &s, vector<compile_server_info> &servers, const string &cert_db_path);
134
c77af0d0
DB
135static string
136private_ssl_cert_db_path (const systemtap_session &s)
137{
138 return s.data_path + "/ssl/client";
139}
140
141static string
142global_ssl_cert_db_path ()
143{
144 return SYSCONFDIR "/systemtap/ssl/client";
145}
146
147static string
148signing_cert_db_path ()
149{
150 return SYSCONFDIR "/systemtap/staprun";
151}
152#endif // HAVE_NSS
153
aa4d21c0
DB
154int
155compile_server_client::passes_0_4 ()
156{
2fad97fd
DB
157 STAP_PROBE1(stap, client__start, &s);
158
159#if ! HAVE_NSS
160 // This code will never be called, if we don't have NSS, but it must still
161 // compile.
3d9dfde6 162 return 1; // Failure
2fad97fd 163#else
aa4d21c0
DB
164 // arguments parsed; get down to business
165 if (s.verbose > 1)
166 clog << "Using a compile server" << endl;
167
aa4d21c0
DB
168 struct tms tms_before;
169 times (& tms_before);
170 struct timeval tv_before;
171 gettimeofday (&tv_before, NULL);
172
2fad97fd
DB
173 // Create the request package.
174 int rc = initialize ();
85007c04 175 if (rc != 0 || pending_interrupts) goto done;
2fad97fd 176 rc = create_request ();
85007c04 177 if (rc != 0 || pending_interrupts) goto done;
2fad97fd 178 rc = package_request ();
85007c04 179 if (rc != 0 || pending_interrupts) goto done;
2fad97fd
DB
180
181 // Submit it to the server.
182 rc = find_and_connect_to_server ();
85007c04 183 if (rc != 0 || pending_interrupts) goto done;
2fad97fd
DB
184
185 // Unpack and process the response.
186 rc = unpack_response ();
85007c04 187 if (rc != 0 || pending_interrupts) goto done;
2fad97fd
DB
188 rc = process_response ();
189
f3fbabf2
DB
190 if (rc == 0 && s.last_pass == 4)
191 {
192 cout << s.module_name + ".ko";
193 cout << endl;
194 }
195
2fad97fd 196 done:
aa4d21c0
DB
197 struct tms tms_after;
198 times (& tms_after);
199 unsigned _sc_clk_tck = sysconf (_SC_CLK_TCK);
200 struct timeval tv_after;
201 gettimeofday (&tv_after, NULL);
202
203#define TIMESPRINT "in " << \
204 (tms_after.tms_cutime + tms_after.tms_utime \
205 - tms_before.tms_cutime - tms_before.tms_utime) * 1000 / (_sc_clk_tck) << "usr/" \
206 << (tms_after.tms_cstime + tms_after.tms_stime \
207 - tms_before.tms_cstime - tms_before.tms_stime) * 1000 / (_sc_clk_tck) << "sys/" \
208 << ((tv_after.tv_sec - tv_before.tv_sec) * 1000 + \
209 ((long)tv_after.tv_usec - (long)tv_before.tv_usec) / 1000) << "real ms."
210
211 // syntax errors, if any, are already printed
212 if (s.verbose)
213 {
920be590 214 clog << "Passes: via server " << s.winning_server << " "
85007c04 215 << getmemusage()
aa4d21c0
DB
216 << TIMESPRINT
217 << endl;
218 }
219
85007c04 220 if (rc == 0)
f3fbabf2 221 {
85007c04
DB
222 // Save the module, if necessary.
223 if (s.last_pass == 4)
224 s.save_module = true;
225
226 // Copy module to the current directory.
227 if (s.save_module && ! pending_interrupts)
228 {
229 string module_src_path = s.tmpdir + "/" + s.module_name + ".ko";
230 string module_dest_path = s.module_name + ".ko";
231 copy_file (module_src_path, module_dest_path, s.verbose > 1);
334df419
DB
232 // Also copy the module signature, it it exists.
233 module_src_path += ".sgn";
234 if (file_exists (module_src_path))
235 {
236 module_dest_path += ".sgn";
237 copy_file(module_src_path, module_dest_path, s.verbose > 1);
238 }
85007c04 239 }
f3fbabf2
DB
240 }
241
aa4d21c0
DB
242 STAP_PROBE1(stap, client__end, &s);
243
244 return rc;
b0e5fa85 245#endif // HAVE_NSS
aa4d21c0
DB
246}
247
3d9dfde6 248#if HAVE_NSS
2fad97fd
DB
249// Initialize a client/server session.
250int
251compile_server_client::initialize ()
252{
253 int rc = 0;
254
255 // Initialize session state
256 argc = 0;
257
c77af0d0
DB
258 // Private location for server certificates.
259 private_ssl_dbs.push_back (private_ssl_cert_db_path (s));
2fad97fd 260
c77af0d0
DB
261 // Additional public location.
262 public_ssl_dbs.push_back (global_ssl_cert_db_path ());
2fad97fd
DB
263
264 // Create a temporary directory to package things in.
265 client_tmpdir = s.tmpdir + "/client";
266 rc = create_dir (client_tmpdir.c_str ());
267 if (rc != 0)
268 {
269 const char* e = strerror (errno);
270 cerr << "ERROR: cannot create temporary directory (\""
271 << client_tmpdir << "\"): " << e
272 << endl;
273 }
274
275 return rc;
276}
277
278// Create the request package.
279int
280compile_server_client::create_request ()
281{
282 int rc = 0;
283
284 // Add the script file or script option
285 if (s.script_file != "")
286 {
287 if (s.script_file == "-")
288 {
289 // Copy the script from stdin
290 string packaged_script_dir = client_tmpdir + "/script";
291 rc = create_dir (packaged_script_dir.c_str ());
292 if (rc != 0)
293 {
294 const char* e = strerror (errno);
295 cerr << "ERROR: cannot create temporary directory "
296 << packaged_script_dir << ": " << e
297 << endl;
298 return rc;
299 }
300 rc = ! copy_file("/dev/stdin", packaged_script_dir + "/-");
f3fbabf2
DB
301 if (rc != 0)
302 return rc;
2fad97fd
DB
303
304 // Name the script in the packaged arguments.
f3fbabf2
DB
305 rc = add_package_arg ("script/-");
306 if (rc != 0)
307 return rc;
2fad97fd
DB
308 }
309 else
310 {
311 // Add the script to our package. This will also name the script
312 // in the packaged arguments.
313 rc = include_file_or_directory ("script", s.script_file);
f3fbabf2
DB
314 if (rc != 0)
315 return rc;
2fad97fd
DB
316 }
317 }
318
319 // Add -I paths. Skip the default directory.
320 if (s.include_arg_start != -1)
321 {
322 unsigned limit = s.include_path.size ();
323 for (unsigned i = s.include_arg_start; i < limit; ++i)
324 {
f3fbabf2
DB
325 rc = add_package_arg ("-I");
326 if (rc != 0)
327 return rc;
328 rc = include_file_or_directory ("tapset", s.include_path[i]);
329 if (rc != 0)
330 return rc;
2fad97fd
DB
331 }
332 }
333
334 // Add other options.
f3fbabf2
DB
335 rc = add_package_args ();
336 if (rc != 0)
337 return rc;
2fad97fd
DB
338
339 // Add the sysinfo file
340 string sysinfo = "sysinfo: " + s.kernel_release + " " + s.architecture;
f3fbabf2 341 rc = write_to_file (client_tmpdir + "/sysinfo", sysinfo);
2fad97fd
DB
342
343 return rc;
344}
345
346// Add the arguments specified on the command line to the server request
347// package, as appropriate.
f3fbabf2 348int
2fad97fd
DB
349compile_server_client::add_package_args ()
350{
fd20a70c 351 // stap arguments to be passed to the server.
f3fbabf2 352 int rc = 0;
2fad97fd
DB
353 unsigned limit = s.server_args.size();
354 for (unsigned i = 0; i < limit; ++i)
f3fbabf2
DB
355 {
356 rc = add_package_arg (s.server_args[i]);
357 if (rc != 0)
358 return rc;
359 }
fd20a70c
DB
360
361 // Script arguments.
362 limit = s.args.size();
363 if (limit > 0) {
364 rc = add_package_arg ("--");
365 if (rc != 0)
366 return rc;
367 for (unsigned i = 0; i < limit; ++i)
368 {
369 rc = add_package_arg (s.args[i]);
370 if (rc != 0)
371 return rc;
372 }
373 }
f3fbabf2 374 return rc;
2fad97fd
DB
375}
376
131f914e
DB
377int
378compile_server_client::add_package_arg (const string &arg)
379{
380 int rc = 0;
381 ostringstream fname;
382 fname << client_tmpdir << "/argv" << ++argc;
383 write_to_file (fname.str (), arg); // NB: No terminating newline
384 return rc;
385}
386
2fad97fd
DB
387// Symbolically link the given file or directory into the client's temp
388// directory under the given subdirectory.
389int
390compile_server_client::include_file_or_directory (
391 const string &subdir, const string &path, const char *option
392)
393{
62f9d949
DB
394 // Must predeclare these because we do use 'goto done' to
395 // exit from error situations.
2fad97fd 396 vector<string> components;
62f9d949
DB
397 string name;
398 int rc;
2fad97fd 399
62f9d949
DB
400 // Canonicalize the given path and remove the leading /.
401 string rpath;
402 char *cpath = canonicalize_file_name (path.c_str ());
2fad97fd
DB
403 if (! cpath)
404 {
62f9d949
DB
405 // It can not be canonicalized. Use the name relative to
406 // the current working directory and let the server deal with it.
407 char cwd[PATH_MAX];
408 if (getcwd (cwd, sizeof (cwd)) == NULL)
409 {
410 rpath = path;
411 rc = 1;
412 goto done;
413 }
414 rpath = string (cwd) + "/" + path;
2fad97fd 415 }
62f9d949 416 else
2fad97fd 417 {
62f9d949
DB
418 // It can be canonicalized. Use the canonicalized name and add this
419 // file or directory to the request package.
420 rpath = cpath;
421 free (cpath);
422
423 // First create the requested subdirectory.
424 name = client_tmpdir + "/" + subdir;
2fad97fd
DB
425 rc = create_dir (name.c_str ());
426 if (rc) goto done;
2fad97fd 427
62f9d949
DB
428 // Now create each component of the path within the sub directory.
429 assert (rpath[0] == '/');
430 tokenize (rpath.substr (1), components, "/");
431 assert (components.size () >= 1);
432 unsigned i;
433 for (i = 0; i < components.size() - 1; ++i)
434 {
435 if (components[i].empty ())
436 continue; // embedded '//'
437 name += "/" + components[i];
438 rc = create_dir (name.c_str ());
439 if (rc) goto done;
440 }
441
442 // Now make a symbolic link to the actual file or directory.
443 assert (i == components.size () - 1);
444 name += "/" + components[i];
445 rc = symlink (rpath.c_str (), name.c_str ());
446 if (rc) goto done;
447 }
2fad97fd
DB
448
449 // Name this file or directory in the packaged arguments along with any
450 // associated option.
451 if (option)
f3fbabf2
DB
452 {
453 rc = add_package_arg (option);
454 if (rc) goto done;
455 }
456
62f9d949 457 rc = add_package_arg (subdir + "/" + rpath.substr (1));
2fad97fd
DB
458
459 done:
2fad97fd
DB
460 if (rc != 0)
461 {
462 const char* e = strerror (errno);
62f9d949
DB
463 cerr << "ERROR: unable to add "
464 << rpath
465 << " to temp directory as "
2fad97fd
DB
466 << name << ": " << e
467 << endl;
468 }
2fad97fd
DB
469 return rc;
470}
471
2fad97fd
DB
472// Package the client's temp directory into a form suitable for sending to the
473// server.
474int
475compile_server_client::package_request ()
476{
477 // Package up the temporary directory into a zip file.
478 client_zipfile = client_tmpdir + ".zip";
479 string cmd = "cd " + client_tmpdir + " && zip -qr " + client_zipfile + " *";
480 int rc = stap_system (s.verbose, cmd);
2fad97fd
DB
481 return rc;
482}
483
484int
485compile_server_client::find_and_connect_to_server ()
486{
2e15a955
DB
487 // Accumulate info on the specified servers.
488 vector<compile_server_info> server_list;
489 get_specified_server_info (s, server_list);
2fad97fd 490
c77af0d0
DB
491 // Ignore the empty first entry.
492 assert (! server_list.empty ());
493 assert (server_list[0].host_name.empty ());
ed1719be 494 server_list.erase (server_list.begin ());
c77af0d0 495
2e15a955
DB
496 // Did we identify any potential servers?
497 unsigned limit = server_list.size ();
498 if (limit == 0)
2fad97fd 499 {
2e15a955
DB
500 cerr << "Unable to find a server" << endl;
501 return 1;
502 }
1f9938b9 503
2e15a955
DB
504 // Now try each of the identified servers in turn.
505 for (unsigned i = 0; i < limit; ++i)
506 {
507 int rc = compile_using_server (server_list[i]);
508 if (rc == 0)
1f9938b9 509 {
2e15a955
DB
510 s.winning_server =
511 server_list[i].host_name + string(" [") +
512 server_list[i].ip_address + string(":") +
513 lex_cast(server_list[i].port) + string("]");
514 return 0; // success!
1f9938b9 515 }
2e15a955 516 }
2fad97fd 517
ed1719be 518 return 1; // Failure - message already generated.
2fad97fd
DB
519}
520
f3fbabf2 521// Temporary until the stap-client-connect program goes away.
2fad97fd
DB
522extern "C"
523int
524client_main (const char *hostName, unsigned short port,
c77af0d0
DB
525 const char* infileName, const char* outfileName,
526 const char* trustNewServer);
3d9dfde6 527#endif // HAVE_NSS
2fad97fd
DB
528
529int
530compile_server_client::compile_using_server (const compile_server_info &server)
531{
f3fbabf2 532 // This code will never be called if we don't have NSS, but it must still
2fad97fd
DB
533 // compile.
534#if HAVE_NSS
0d1534e8
DB
535 // We cannot contact the server if we don't have the port number.
536 if (server.port == 0)
537 return 1; // Failure
538
b0e5fa85
DB
539 // Make sure NSPR is initialized
540 s.NSPR_init ();
541
1f9938b9 542 // Attempt connection using each of the available client certificate
ed1719be
DB
543 // databases. Assume the server certificate is invalid until proven otherwise.
544 PR_SetError (SEC_ERROR_CA_CERT_INVALID, 0);
2fad97fd
DB
545 vector<string> dbs = private_ssl_dbs;
546 vector<string>::iterator i = dbs.end();
547 dbs.insert (i, public_ssl_dbs.begin (), public_ssl_dbs.end ());
b0e5fa85 548 int rc = 1; // assume failure
2fad97fd
DB
549 for (i = dbs.begin (); i != dbs.end (); ++i)
550 {
ed1719be
DB
551 // Make sure the database directory exists. It is not an error if it
552 // doesn't.
553 if (! file_exists (*i))
554 continue;
2fad97fd 555
ed1719be 556 const char *cert_dir = i->c_str ();
2fad97fd 557 if (s.verbose > 1)
1f9938b9 558 clog << "Attempting SSL connection with " << server << endl
2fad97fd
DB
559 << " using certificates from the database in " << cert_dir
560 << endl;
2fad97fd 561
f3fbabf2
DB
562#if 0 // no client authentication for now.
563 // Set our password function callback.
2fad97fd
DB
564 PK11_SetPasswordFunc (myPasswd);
565#endif
566
f3fbabf2 567 // Initialize the NSS libraries.
2fad97fd
DB
568 SECStatus secStatus = NSS_InitReadWrite (cert_dir);
569 if (secStatus != SECSuccess)
570 {
f3fbabf2 571 // Try it again, readonly.
2fad97fd
DB
572 secStatus = NSS_Init(cert_dir);
573 if (secStatus != SECSuccess)
574 {
575 cerr << "Error initializing NSS" << endl;
b0e5fa85
DB
576 nssError ();
577 NSS_Shutdown();
578 continue; // try next database
2fad97fd
DB
579 }
580 }
581
f3fbabf2 582 // All cipher suites except RSA_NULL_MD5 are enabled by Domestic Policy.
2fad97fd
DB
583 NSS_SetDomesticPolicy ();
584
585 server_zipfile = s.tmpdir + "/server.zip";
b0e5fa85
DB
586 rc = client_main (server.host_name.c_str (), server.port,
587 client_zipfile.c_str(), server_zipfile.c_str (),
588 NULL/*trustNewServer_p*/);
589
2fad97fd 590 NSS_Shutdown();
2fad97fd 591
ed1719be 592 if (rc == SECSuccess)
b0e5fa85 593 break; // Success!
2fad97fd
DB
594 }
595
ed1719be
DB
596 if (rc != SECSuccess)
597 {
598 cerr << "Unable to connect to a server: ";
599 nssError ();
600 }
601
b0e5fa85 602 return rc;
2fad97fd
DB
603#endif // HAVE_NSS
604
605 return 1; // Failure
606}
607
3d9dfde6 608#if HAVE_NSS
2fad97fd
DB
609int
610compile_server_client::unpack_response ()
611{
f3fbabf2 612 // Unzip the response package.
2fad97fd
DB
613 server_tmpdir = s.tmpdir + "/server";
614 string cmd = "unzip -qd " + server_tmpdir + " " + server_zipfile;
615 int rc = stap_system (s.verbose, cmd);
f3fbabf2
DB
616 if (rc != 0)
617 {
618 cerr << "Unable to unzip the server reponse '" << server_zipfile << '\''
619 << endl;
620 }
2fad97fd
DB
621
622 // If the server's response contains a systemtap temp directory, move
623 // its contents to our temp directory.
624 glob_t globbuf;
625 string filespec = server_tmpdir + "/stap??????";
1f9938b9 626 if (s.verbose > 2)
2fad97fd
DB
627 clog << "Searching \"" << filespec << "\"" << endl;
628 int r = glob(filespec.c_str (), 0, NULL, & globbuf);
629 if (r != GLOB_NOSPACE && r != GLOB_ABORTED && r != GLOB_NOMATCH)
630 {
631 if (globbuf.gl_pathc > 1)
632 {
633 cerr << "Incorrect number of files in server response" << endl;
f3fbabf2 634 rc = 1; goto done;
2fad97fd
DB
635 }
636
637 assert (globbuf.gl_pathc == 1);
638 string dirname = globbuf.gl_pathv[0];
1f9938b9 639 if (s.verbose > 2)
2fad97fd
DB
640 clog << " found " << dirname << endl;
641
642 filespec = dirname + "/*";
1f9938b9 643 if (s.verbose > 2)
2fad97fd
DB
644 clog << "Searching \"" << filespec << "\"" << endl;
645 int r = glob(filespec.c_str (), GLOB_PERIOD, NULL, & globbuf);
646 if (r != GLOB_NOSPACE && r != GLOB_ABORTED && r != GLOB_NOMATCH)
647 {
2fad97fd
DB
648 unsigned prefix_len = dirname.size () + 1;
649 for (unsigned i = 0; i < globbuf.gl_pathc; ++i)
650 {
651 string oldname = globbuf.gl_pathv[i];
652 if (oldname.substr (oldname.size () - 2) == "/." ||
653 oldname.substr (oldname.size () - 3) == "/..")
654 continue;
655 string newname = s.tmpdir + "/" + oldname.substr (prefix_len);
1f9938b9 656 if (s.verbose > 2)
2fad97fd
DB
657 clog << " found " << oldname
658 << " -- linking from " << newname << endl;
f3fbabf2
DB
659 rc = symlink (oldname.c_str (), newname.c_str ());
660 if (rc != 0)
661 {
662 cerr << "Unable to link '" << oldname
663 << "' to '" << newname << "': "
664 << strerror (errno) << endl;
665 goto done;
666 }
2fad97fd
DB
667 }
668 }
669 }
670
f3fbabf2
DB
671 // Remove the output line due to the synthetic server-side -k
672 cmd = "sed -i '/^Keeping temporary directory.*/ d' " +
673 server_tmpdir + "/stderr";
674 stap_system (s.verbose, cmd);
2fad97fd 675
f3fbabf2
DB
676 // Remove the output line due to the synthetic server-side -p4
677 cmd = "sed -i '/^.*\\.ko$/ d' " + server_tmpdir + "/stdout";
678 stap_system (s.verbose, cmd);
2fad97fd 679
2fad97fd
DB
680 done:
681 globfree (& globbuf);
682 return rc;
683}
684
685int
686compile_server_client::process_response ()
687{
688 // Pick up the results of running stap on the server.
2fad97fd 689 string filename = server_tmpdir + "/rc";
f3fbabf2
DB
690 int stap_rc;
691 int rc = read_from_file (filename, stap_rc);
692 if (rc != 0)
693 return rc;
85007c04 694 rc = stap_rc;
2fad97fd 695
2fad97fd
DB
696 if (s.last_pass >= 4)
697 {
698 // The server should have returned a module.
699 string filespec = s.tmpdir + "/*.ko";
1f9938b9 700 if (s.verbose > 2)
2fad97fd 701 clog << "Searching \"" << filespec << "\"" << endl;
f3fbabf2
DB
702
703 glob_t globbuf;
2fad97fd
DB
704 int r = glob(filespec.c_str (), 0, NULL, & globbuf);
705 if (r != GLOB_NOSPACE && r != GLOB_ABORTED && r != GLOB_NOMATCH)
706 {
707 if (globbuf.gl_pathc > 1)
f3fbabf2
DB
708 cerr << "Incorrect number of modules in server response" << endl;
709 else
2fad97fd 710 {
f3fbabf2
DB
711 assert (globbuf.gl_pathc == 1);
712 string modname = globbuf.gl_pathv[0];
1f9938b9 713 if (s.verbose > 2)
f3fbabf2
DB
714 clog << " found " << modname << endl;
715
716 // If a module name was not specified by the user, then set it to
717 // be the one generated by the server.
718 if (! s.save_module)
719 {
720 vector<string> components;
721 tokenize (modname, components, "/");
722 s.module_name = components.back ();
723 s.module_name.erase(s.module_name.size() - 3);
724 }
2fad97fd
DB
725 }
726 }
727 else if (s.have_script)
728 {
85007c04 729 if (rc == 0)
2fad97fd
DB
730 {
731 cerr << "No module was returned by the server" << endl;
85007c04 732 rc = 1;
2fad97fd
DB
733 }
734 }
f3fbabf2 735 globfree (& globbuf);
2fad97fd
DB
736 }
737
f3fbabf2 738 // Output stdout and stderr.
2fad97fd 739 filename = server_tmpdir + "/stderr";
f3fbabf2 740 flush_to_stream (filename, cerr);
2fad97fd
DB
741
742 filename = server_tmpdir + "/stdout";
f3fbabf2
DB
743 flush_to_stream (filename, cout);
744
745 return rc;
746}
747
748int
749compile_server_client::read_from_file (const string &fname, int &data)
750{
131f914e
DB
751 // C++ streams may not set errno in the even of a failure. However if we
752 // set it to 0 before each operation and it gets set during the operation,
753 // then we can use its value in order to determine what happened.
754 errno = 0;
755 ifstream f (fname.c_str ());
756 if (! f.good ())
f3fbabf2 757 {
131f914e
DB
758 cerr << "Unable to open file '" << fname << "' for reading: ";
759 goto error;
f3fbabf2
DB
760 }
761
131f914e
DB
762 // Read the data;
763 errno = 0;
764 f >> data;
765 if (f.fail ())
f3fbabf2 766 {
131f914e
DB
767 cerr << "Unable to read from file '" << fname << "': ";
768 goto error;
f3fbabf2
DB
769 }
770
920be590 771 // NB: not necessary to f.close ();
131f914e
DB
772 return 0; // Success
773
774 error:
775 if (errno)
776 cerr << strerror (errno) << endl;
777 else
778 cerr << "unknown error" << endl;
779 return 1; // Failure
f3fbabf2
DB
780}
781
782int
783compile_server_client::write_to_file (const string &fname, const string &data)
784{
131f914e
DB
785 // C++ streams may not set errno in the even of a failure. However if we
786 // set it to 0 before each operation and it gets set during the operation,
787 // then we can use its value in order to determine what happened.
788 errno = 0;
789 ofstream f (fname.c_str ());
790 if (! f.good ())
f3fbabf2 791 {
131f914e
DB
792 cerr << "Unable to open file '" << fname << "' for writing: ";
793 goto error;
f3fbabf2
DB
794 }
795
131f914e
DB
796 // Write the data;
797 f << data;
798 errno = 0;
799 if (f.fail ())
f3fbabf2 800 {
131f914e
DB
801 cerr << "Unable to write to file '" << fname << "': ";
802 goto error;
f3fbabf2
DB
803 }
804
920be590 805 // NB: not necessary to f.close ();
131f914e
DB
806 return 0; // Success
807
808 error:
809 if (errno)
810 cerr << strerror (errno) << endl;
811 else
812 cerr << "unknown error" << endl;
813 return 1; // Failure
f3fbabf2
DB
814}
815
816int
817compile_server_client::flush_to_stream (const string &fname, ostream &o)
818{
131f914e
DB
819 // C++ streams may not set errno in the even of a failure. However if we
820 // set it to 0 before each operation and it gets set during the operation,
821 // then we can use its value in order to determine what happened.
822 errno = 0;
823 ifstream f (fname.c_str ());
824 if (! f.good ())
f3fbabf2 825 {
131f914e
DB
826 cerr << "Unable to open file '" << fname << "' for reading: ";
827 goto error;
f3fbabf2
DB
828 }
829
131f914e 830 // Stream the data
920be590
FCE
831
832 // NB: o << f.rdbuf() misbehaves for some reason, appearing to close o,
833 // which is unfortunate if o == cerr or cout.
834 while (1)
f3fbabf2 835 {
920be590
FCE
836 errno = 0;
837 int c = f.get();
838 if (f.eof ()) return 0; // normal exit
839 if (! f.good()) break;
840 o.put(c);
841 if (! o.good()) break;
f3fbabf2 842 }
2fad97fd 843
920be590 844 // NB: not necessary to f.close ();
131f914e
DB
845
846 error:
847 if (errno)
848 cerr << strerror (errno) << endl;
849 else
850 cerr << "unknown error" << endl;
851 return 1; // Failure
2fad97fd 852}
3d9dfde6 853#endif // HAVE_NSS
131f914e 854
85007c04 855// Utility Functions.
131f914e 856//-----------------------------------------------------------------------
b0e5fa85 857ostream &operator<< (ostream &s, const compile_server_info &i)
85007c04 858{
1f9938b9 859 s << i.host_name;
0d1534e8 860 s << " ip=";
1f9938b9 861 if (! i.ip_address.empty ())
0d1534e8
DB
862 s << i.ip_address;
863 else
864 s << "unknown";
865 s << " port=";
1f9938b9 866 if (i.port != 0)
0d1534e8
DB
867 s << i.port;
868 else
1a7f7b3f 869 s << "offline";
0d1534e8 870 s << " sysinfo=\"";
1f9938b9 871 if (! i.sysinfo.empty ())
0d1534e8 872 s << i.sysinfo << '"';
2e15a955 873 else
0d1534e8
DB
874 s << "unknown\"";
875 s << " certinfo=\"";
876 if (! i.certinfo.empty ())
877 s << i.certinfo << '"';
878 else
879 s << "unknown\"";
1f9938b9 880 return s;
85007c04
DB
881}
882
2e15a955
DB
883// Return the default server specification, used when none is given on the
884// command line.
885static string
886default_server_spec (const systemtap_session &s)
85007c04 887{
eb3a0eee 888 // If the --use-server option has been used
85007c04
DB
889 // the default is 'specified'
890 // otherwise if the --unprivileged has been used
891 // the default is online,trusted,compatible,signer
892 // otherwise
1e7630bf 893 // the default is online,compatible
85007c04
DB
894 //
895 // Having said that,
0d1534e8
DB
896 // 'online' and 'compatible' will only succeed if we have avahi
897 // 'trusted' and 'signer' will only succeed if we have NSS
898 //
899 string working_string = "online,trusted,compatible";
2e15a955 900 if (s.unprivileged)
0d1534e8 901 working_string += ",signer";
2e15a955
DB
902 return working_string;
903}
85007c04 904
2e15a955
DB
905static int
906server_spec_to_pmask (const string &server_spec)
907{
85007c04
DB
908 // Construct a mask of the server properties that have been requested.
909 // The available properties are:
0d1534e8
DB
910 // trusted - servers which are trusted SSL peers.
911 // online - online servers.
85007c04
DB
912 // compatible - servers which compile for the current kernel release
913 // and architecture.
914 // signer - servers which are trusted module signers.
eb3a0eee 915 // specified - servers which have been specified using --use-server=XXX.
85007c04 916 // If no servers have been specified, then this is
eb3a0eee 917 // equivalent to --list-servers=trusted,online,compatible.
0d1534e8
DB
918 // all - all trusted servers, trusted module signers,
919 // servers currently online and specified servers.
920 string working_spec = server_spec;
85007c04 921 vector<string> properties;
0d1534e8 922 tokenize (working_spec, properties, ",");
85007c04
DB
923 int pmask = 0;
924 unsigned limit = properties.size ();
925 for (unsigned i = 0; i < limit; ++i)
926 {
927 const string &property = properties[i];
0d1534e8
DB
928 // Tolerate (and ignore) empty properties.
929 if (property.empty ())
930 continue;
85007c04
DB
931 if (property == "all")
932 {
0d1534e8 933 pmask |= compile_server_all;
85007c04
DB
934 }
935 else if (property == "specified")
936 {
937 pmask |= compile_server_specified;
938 }
85007c04
DB
939 else if (property == "trusted")
940 {
941 pmask |= compile_server_trusted;
942 }
85007c04
DB
943 else if (property == "online")
944 {
945 pmask |= compile_server_online;
946 }
85007c04
DB
947 else if (property == "compatible")
948 {
949 pmask |= compile_server_compatible;
950 }
85007c04
DB
951 else if (property == "signer")
952 {
953 pmask |= compile_server_signer;
954 }
85007c04
DB
955 else
956 {
957 cerr << "Warning: unsupported compile server property: " << property
958 << endl;
959 }
960 }
2e15a955
DB
961 return pmask;
962}
963
964void
965query_server_status (systemtap_session &s)
966{
b0e5fa85
DB
967 // Make sure NSPR is initialized
968 s.NSPR_init ();
969
2e15a955
DB
970 unsigned limit = s.server_status_strings.size ();
971 for (unsigned i = 0; i < limit; ++i)
972 query_server_status (s, s.server_status_strings[i]);
973}
974
b0e5fa85 975static void
2e15a955
DB
976query_server_status (systemtap_session &s, const string &status_string)
977{
2e15a955
DB
978 // If this string is empty, then the default is "specified"
979 string working_string = status_string;
980 if (working_string.empty ())
981 working_string = "specified";
982
0d1534e8
DB
983 // If the query is "specified" and no servers have been specified
984 // (i.e. --use-server not used or used once with no argument), then
985 // use the default query.
986 if (working_string == "specified" &&
987 (s.specified_servers.empty () ||
988 (s.specified_servers.size () == 1 && s.specified_servers[0].empty ())))
2e15a955
DB
989 working_string = default_server_spec (s);
990
2e15a955 991 int pmask = server_spec_to_pmask (working_string);
85007c04
DB
992
993 // Now obtain a list of the servers which match the criteria.
994 vector<compile_server_info> servers;
995 get_server_info (s, pmask, servers);
996
c77af0d0 997 // Print the server information. Skip tge emoty entry at the head of the list.
0d1534e8
DB
998 clog << "Systemtap Compile Server Status for '" << working_string << '\''
999 << endl;
2e15a955 1000 unsigned limit = servers.size ();
c77af0d0 1001 for (unsigned i = 1; i < limit; ++i)
85007c04
DB
1002 {
1003 clog << servers[i] << endl;
1004 }
85007c04
DB
1005}
1006
c77af0d0
DB
1007// Add or remove trust of the servers specified on the command line.
1008void
1009manage_server_trust (systemtap_session &s)
1010{
b0e5fa85
DB
1011 // This function will never be called if we don't have NSS, but it must
1012 // still compile.
c77af0d0
DB
1013#if HAVE_NSS
1014 // Nothing to do if --trust-servers was not specified.
1015 if (s.server_trust_spec.empty ())
1016 return;
1017
1018 // Break up and analyze the trust specification. Recognized components are:
1019 // ssl - trust the specified servers as ssl peers
1020 // signer - trust the specified servers as module signers
1021 // revoke - revoke the requested trust
1022 // all-users - apply/revoke the requested trust for all users
1023 vector<string>components;
1024 tokenize (s.server_trust_spec, components, ",");
1025 bool ssl = false;
1026 bool signer = false;
1027 bool revoke = false;
1028 bool all_users = false;
1029 bool error = false;
1030 for (vector<string>::const_iterator i = components.begin ();
1031 i != components.end ();
1032 ++i)
1033 {
1034 if (*i == "ssl")
1035 ssl = true;
1036 else if (*i == "signer")
1037 {
1038 if (geteuid () != 0)
1039 {
1040 cerr << "Only root can specify 'signer' on --trust-servers" << endl;
1041 error = true;
1042 }
1043 else
1044 signer = true;
1045 }
1046 else if (*i == "revoke")
1047 revoke = true;
1048 else if (*i == "all-users")
1049 {
1050 if (geteuid () != 0)
1051 {
1052 cerr << "Only root can specify 'all-users' on --trust-servers" << endl;
1053 error = true;
1054 }
1055 else
1056 all_users = true;
1057 }
1058 else
1059 cerr << "Warning: Unrecognized server trust specification: " << *i
1060 << endl;
1061 }
1062 if (error)
1063 return;
1064
b0e5fa85
DB
1065 // Make sure NSPR is initialized
1066 s.NSPR_init ();
1067
c77af0d0
DB
1068 // Now obtain the list of specified servers.
1069 vector<compile_server_info> server_list;
1a7f7b3f 1070 get_specified_server_info (s, server_list, true/*no_default*/);
c77af0d0
DB
1071
1072 // Ignore the empty first entry.
1073 assert (! server_list.empty ());
1074 assert (server_list[0].host_name.empty ());
334df419 1075 server_list.erase (server_list.begin ());
c77af0d0
DB
1076
1077 // Did we identify any potential servers? Ignore the empty first entry.
1078 unsigned limit = server_list.size ();
1079 if (limit == 0)
1080 {
1081 cerr << "No servers identified for trust" << endl;
1082 return;
1083 }
1084
1085 // Create a string representing the request in English.
1086 // If neither 'ssl' or 'signer' was specified, the default is 'ssl'.
1087 if (! ssl && ! signer)
1088 ssl = true;
1089 ostringstream trustString;
1090 if (ssl)
1091 {
1092 trustString << "as an SSL peer";
1093 if (all_users)
1094 trustString << " for all users";
1095 else
1096 trustString << " for the current user";
1097 }
1098 if (signer)
1099 {
1100 if (ssl)
1101 trustString << " and ";
1102 trustString << "as a module signer for all users";
1103 }
1104
1105 // Prompt the user to confirm what's about to happen.
1106 if (revoke)
1107 clog << "Revoke trust ";
1108 else
1109 clog << "Add trust ";
1110 clog << "in the following servers " << trustString.str () << '?' << endl;
1111 for (unsigned i = 0; i < limit; ++i)
1112 clog << " " << server_list[i] << endl;
334df419
DB
1113 clog << "[y/N] " << flush;
1114
1115 // Only carry out the operation if the response is "yes"
1116 string response;
1117 cin >> response;
1118 if (response[0] != 'y' && response [0] != 'Y')
1119 {
1120 clog << "Server trust unchanged" << endl;
1121 return;
1122 }
c77af0d0
DB
1123
1124 // Now add/revoke the requested trust.
1125 string cert_db_path;
1126 if (ssl)
1127 {
1128 if (all_users)
1129 cert_db_path = global_ssl_cert_db_path ();
1130 else
1131 cert_db_path = private_ssl_cert_db_path (s);
1132 if (revoke)
1133 revoke_server_trust (s, cert_db_path, server_list);
1134 else
1135 add_server_trust (s, cert_db_path, server_list);
1136 }
1137 if (signer)
1138 {
1139 cert_db_path = signing_cert_db_path ();
1140 if (revoke)
1141 revoke_server_trust (s, cert_db_path, server_list);
1142 else
1143 add_server_trust (s, cert_db_path, server_list);
1144 }
1145#endif // HAVE_NSS
1146}
1147
3d9dfde6 1148#if HAVE_NSS
c77af0d0
DB
1149// Issue a status message for when a server's trust is already in place.
1150static void
1151trust_already_in_place (
1152 const compile_server_info &server,
1153 const string cert_db_path,
1154 bool revoking
1155)
1156{
1157 string purpose;
1158 if (cert_db_path == signing_cert_db_path ())
1159 purpose = "as a module signer for all users";
1160 else
1161 {
1162 purpose = "as an SSL peer";
1163 if (cert_db_path == global_ssl_cert_db_path ())
1164 purpose += " for all users";
1165 else
1166 purpose += " for the current user";
1167 }
1168
1169 clog << server << " is already ";
1170 if (revoking)
1171 clog << "un";
1172 clog << "trusted " << purpose << endl;
1173}
1174
1175// Add the given servers to the given database of trusted servers.
b0e5fa85 1176static void
c77af0d0
DB
1177add_server_trust (
1178 systemtap_session &s,
1179 const string &cert_db_path,
1180 const vector<compile_server_info> &server_list
1181)
1182{
c77af0d0
DB
1183 // Make sure the given path exists.
1184 if (create_dir (cert_db_path.c_str (), 0755) != 0)
1185 {
1186 cerr << "Unable to find or create the client certificate database directory "
1187 << cert_db_path << ": ";
1188 perror ("");
1189 return;
1190 }
1191
c77af0d0
DB
1192 // Initialize the NSS libraries -- read/write
1193 SECStatus secStatus = NSS_InitReadWrite (cert_db_path.c_str ());
1194 if (secStatus != SECSuccess)
1195 {
1196 cerr << "Error initializing NSS for " << cert_db_path << endl;
1197 nssError ();
1198 goto cleanup;
1199 }
1200
1201 // All cipher suites except RSA_NULL_MD5 are enabled by Domestic Policy.
1202 NSS_SetDomesticPolicy ();
1203
1204 // Iterate over the servers to become trusted. Contact each one and
1205 // add it to the list of trusted servers if it is not already trusted.
1206 // client_main will issue any error messages.
1207 for (vector<compile_server_info>::const_iterator server = server_list.begin();
1208 server != server_list.end ();
1209 ++server)
1210 {
1211 client_main (server->host_name.c_str (), server->port,
1212 NULL, NULL, "permanent");
1213 }
1214
1215 cleanup:
1a7f7b3f 1216 // Shutdown NSS.
c77af0d0 1217 NSS_Shutdown ();
1a7f7b3f
DB
1218
1219 // Make sure the database files are readable.
1220 glob_t globbuf;
1221 string filespec = cert_db_path + "/*.db";
1222 if (s.verbose > 2)
1223 clog << "Searching \"" << filespec << "\"" << endl;
1224 int r = glob (filespec.c_str (), 0, NULL, & globbuf);
1225 if (r != GLOB_NOSPACE && r != GLOB_ABORTED && r != GLOB_NOMATCH)
1226 {
1227 for (unsigned i = 0; i < globbuf.gl_pathc; ++i)
1228 {
1229 string filename = globbuf.gl_pathv[i];
1230 if (s.verbose > 2)
1231 clog << " found " << filename << endl;
1232
1233 if (chmod (filename.c_str (), 0644) != 0)
1234 {
1235 cerr << "Warning: Unable to change permissions on "
1236 << filename << ": ";
1237 perror ("");
1238 }
1239 }
1240 }
c77af0d0
DB
1241}
1242
1243// Remove the given servers from the given database of trusted servers.
b0e5fa85 1244static void
c77af0d0
DB
1245revoke_server_trust (
1246 systemtap_session &s,
1247 const string &cert_db_path,
1248 const vector<compile_server_info> &server_list
1249)
1250{
c77af0d0
DB
1251 // Make sure the given path exists.
1252 if (! file_exists (cert_db_path))
1253 {
1254 if (s.verbose > 1)
1255 cerr << "Certificate database '" << cert_db_path << "' does not exist."
1256 << endl;
1257 for (vector<compile_server_info>::const_iterator server = server_list.begin();
1258 server != server_list.end ();
1259 ++server)
1260 {
1261 trust_already_in_place (*server, cert_db_path, true/*revoking*/);
1262 }
1263 return;
1264 }
1265
1266 // Must predeclare these because of jumps to cleanup: below.
1267 PK11SlotInfo *slot = NULL;
1268 CERTCertDBHandle *handle;
1269 PRArenaPool *tmpArena = NULL;
1270 CERTCertList *certs = NULL;
1271 CERTCertificate *db_cert;
1272
c77af0d0
DB
1273 // Initialize the NSS libraries -- read/write
1274 SECStatus secStatus = NSS_InitReadWrite (cert_db_path.c_str ());
1275 if (secStatus != SECSuccess)
1276 {
1277 cerr << "Error initializing NSS for " << cert_db_path << endl;
1278 nssError ();
1279 goto cleanup;
1280 }
1281 slot = PK11_GetInternalKeySlot ();
1282 handle = CERT_GetDefaultCertDB();
1283
1284 // A memory pool to work in
1285 tmpArena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
1286 if (! tmpArena)
1287 {
1288 cerr << "Out of memory:";
1289 nssError ();
1290 goto cleanup;
1291 }
1292
1293 // Iterate over the servers to become untrusted.
1294 for (vector<compile_server_info>::const_iterator server = server_list.begin();
1295 server != server_list.end ();
1296 ++server)
1297 {
1298 // If the server's certificate serial number is unknown, then we can't
1299 // match it with one in the database.
1300 if (server->certinfo.empty ())
1301 {
1302 cerr << "Unable to obtain certificate information for server "
1303 << *server << endl;
1304 continue;
1305 }
1306
1307 // Search the client-side database of trusted servers.
1308 db_cert = PK11_FindCertFromNickname (server_cert_nickname, NULL);
1309 if (! db_cert)
1310 {
1311 // No trusted servers. Not an error, but issue a status message.
1312 trust_already_in_place (*server, cert_db_path, true/*revoking*/);
1313 continue;
1314 }
1315
1316 // Here, we have one cert with the desired nickname.
1317 // Now, we will attempt to get a list of ALL certs
1318 // with the same subject name as the cert we have. That list
1319 // should contain, at a minimum, the one cert we have already found.
1320 // If the list of certs is empty (NULL), the libraries have failed.
1321 certs = CERT_CreateSubjectCertList (NULL, handle, & db_cert->derSubject,
1322 PR_Now (), PR_FALSE);
1323 CERT_DestroyCertificate (db_cert);
1324 if (! certs)
1325 {
1326 cerr << "Unable to query certificate database " << cert_db_path
1327 << ": " << endl;
1328 PORT_SetError (SEC_ERROR_LIBRARY_FAILURE);
1329 nssError ();
1330 goto cleanup;
1331 }
1332
1333 // Find the certificate matching the one belonging to our server.
1334 CERTCertListNode *node;
1335 for (node = CERT_LIST_HEAD (certs);
1336 ! CERT_LIST_END (node, certs);
1337 node = CERT_LIST_NEXT (node))
1338 {
1339 // The certificate we're working with.
1340 db_cert = node->cert;
1341
1342 // Get the serial number.
1343 ostringstream serialNumber;
334df419 1344 serialNumber << hex << setfill('0') << right;
c77af0d0
DB
1345 for (unsigned i = 0; i < db_cert->serialNumber.len; ++i)
1346 {
1347 if (i > 0)
1348 serialNumber << ':';
334df419 1349 serialNumber << setw(2) << (unsigned)db_cert->serialNumber.data[i];
c77af0d0
DB
1350 }
1351
1352 // Does the serial number match that of the current server?
1353 if (serialNumber.str () != server->certinfo)
1354 continue; // goto next certificate
1355
1356 // As a sanity check, make sure the host name on the certificate
1357 // matches that of our server.
1358 // The host name is in the alt-name extension of the certificate.
1359 SECItem subAltName;
1360 subAltName.data = NULL;
1361 secStatus = CERT_FindCertExtension (db_cert,
1362 SEC_OID_X509_SUBJECT_ALT_NAME,
1363 & subAltName);
1364 if (secStatus != SECSuccess || ! subAltName.data)
1365 {
1366 cerr << "Unable to find alt name extension on server certificate: ";
1367 nssError ();
1368 continue;
1369 }
1370
1371 // Decode the extension.
1372 CERTGeneralName *nameList = CERT_DecodeAltNameExtension (tmpArena, & subAltName);
1373 SECITEM_FreeItem(& subAltName, PR_FALSE);
1374 if (! nameList)
1375 {
1376 cerr << "Unable to decode alt name extension on server certificate: ";
1377 nssError ();
1378 continue;
1379 }
1380
ed1719be
DB
1381 // Match any one of the alt names in the extension.
1382 do
1383 {
1384 assert (nameList->type == certDNSName);
1385 string host_name = string ((const char *)nameList->name.other.data,
1386 nameList->name.other.len);
1387 if (host_name == server->host_name)
1388 break;
1389 nameList = CERT_GetNextGeneralName (nameList);
1390 }
1391 while (nameList);
1392 // Don't need to free nameList. It's part of the tmpArena.
c77af0d0 1393
ed1719be
DB
1394 // Did we find a match?
1395 if (! nameList)
c77af0d0 1396 {
ed1719be 1397 cerr << "Host name on certificate does not match the host name '"
c77af0d0
DB
1398 << server->host_name << "' of the server"
1399 << endl;
1400 continue;
1401 }
1402
1403 // All is ok! Remove the certificate from the database.
1404 break;
1405 } // Loop over certificates in the database
1406
1407 // Was a certificate matching the server found? */
1408 if (CERT_LIST_END (node, certs))
1409 {
1410 // Not found. Server is already untrusted.
1411 trust_already_in_place (*server, cert_db_path, true/*revoking*/);
1412 }
1413 else
1414 {
1415 secStatus = SEC_DeletePermCertificate (db_cert);
1416 if (secStatus != SECSuccess)
1417 {
1418 cerr << "Unable to remove certificate from " << cert_db_path
1419 << ": " << endl;
1420 nssError ();
1421 }
1422 }
1423 CERT_DestroyCertList (certs);
1424 certs = NULL;
1425 } // Loop over servers
1426
1427 cleanup:
1428 if (certs)
1429 CERT_DestroyCertList (certs);
1430 if (slot)
1431 PK11_FreeSlot (slot);
1432 if (tmpArena)
1433 PORT_FreeArena (tmpArena, PR_FALSE);
1434
1435 NSS_Shutdown ();
c77af0d0 1436}
3d9dfde6 1437#endif // HAVE_NSS
c77af0d0 1438
b0e5fa85 1439static void
85007c04 1440get_server_info (
1f9938b9 1441 systemtap_session &s,
85007c04
DB
1442 int pmask,
1443 vector<compile_server_info> &servers
1444)
1445{
1446 // Get information on compile servers matching the requested criteria.
0d1534e8 1447 // The order of queries is significant. Accumulating queries must go first
1a7f7b3f 1448 // followed by accumulating/filtering queries followed by filtering queries.
0d1534e8
DB
1449 // We start with an empty vector.
1450 // These queries accumulate server information.
1451 vector<compile_server_info> temp;
1452 if ((pmask & compile_server_all))
1453 {
1454 get_all_server_info (s, temp);
1455 }
2e15a955
DB
1456 if ((pmask & compile_server_specified))
1457 {
0d1534e8 1458 get_specified_server_info (s, temp);
2e15a955 1459 }
0d1534e8
DB
1460 // These queries filter server information if the vector is not empty and
1461 // accumulate it otherwise.
85007c04
DB
1462 if ((pmask & compile_server_online))
1463 {
0d1534e8
DB
1464 get_or_keep_online_server_info (s, temp);
1465 }
1466 if ((pmask & compile_server_trusted))
1467 {
1468 get_or_keep_trusted_server_info (s, temp);
85007c04 1469 }
0d1534e8
DB
1470 if ((pmask & compile_server_signer))
1471 {
1472 get_or_keep_signing_server_info (s, temp);
1473 }
1474 // This query filters server information.
85007c04
DB
1475 if ((pmask & compile_server_compatible))
1476 {
0d1534e8 1477 keep_compatible_server_info (s, temp);
85007c04 1478 }
0d1534e8
DB
1479
1480 // Now add the collected information to the target vector.
1481 merge_server_info (temp, servers);
1482}
1483
1484// Get information about all online servers as well as servers trusted
1485// as SSL peers and servers trusted as signers.
b0e5fa85 1486static void
0d1534e8
DB
1487get_all_server_info (
1488 systemtap_session &s,
1489 vector<compile_server_info> &servers
1490)
1491{
1492 // The get_or_keep_XXXX_server_info functions filter the vector
1493 // if it is not empty. So use an empty vector for each query.
1494 vector<compile_server_info> temp;
1495 get_or_keep_online_server_info (s, temp);
1496 merge_server_info (temp, servers);
1497
1498 temp.clear ();
1499 get_or_keep_trusted_server_info (s, temp);
1500 merge_server_info (temp, servers);
1501
1502 temp.clear ();
1503 get_or_keep_signing_server_info (s, temp);
1504 merge_server_info (temp, servers);
85007c04
DB
1505}
1506
b0e5fa85 1507static void
2e15a955
DB
1508get_default_server_info (
1509 systemtap_session &s,
1510 vector<compile_server_info> &servers
1511)
1f9938b9 1512{
2e15a955
DB
1513 // We only need to obtain this once. This is a good thing(tm) since
1514 // obtaining this information is expensive.
1515 static vector<compile_server_info> default_servers;
1516 if (default_servers.empty ())
1f9938b9 1517 {
c77af0d0
DB
1518 // Get the required information.
1519 // get_server_info will add an empty entry at the beginning to indicate
1520 // that the search has been performed, in case the search comes up empty.
0d1534e8
DB
1521 int pmask = server_spec_to_pmask (default_server_spec (s));
1522 get_server_info (s, pmask, default_servers);
2e15a955 1523 }
1f9938b9 1524
c77af0d0 1525 // Add the information, but not duplicates.
2e15a955 1526 unsigned limit = default_servers.size ();
c77af0d0 1527 for (unsigned i = 0; i < limit; ++i)
2e15a955
DB
1528 add_server_info (default_servers[i], servers);
1529}
1f9938b9 1530
b0e5fa85 1531static void
2e15a955
DB
1532get_specified_server_info (
1533 systemtap_session &s,
1a7f7b3f
DB
1534 vector<compile_server_info> &servers,
1535 bool no_default
2e15a955
DB
1536)
1537{
1538 // We only need to obtain this once. This is a good thing(tm) since
1539 // obtaining this information is expensive.
1540 static vector<compile_server_info> specified_servers;
1541 if (specified_servers.empty ())
1542 {
0d1534e8
DB
1543 // Maintain an empty entry to indicate that this search has been
1544 // performed, in case the search comes up empty.
1545 specified_servers.push_back (compile_server_info ());
1546
2e15a955
DB
1547 // If --use-servers was not specified at all, then return info for the
1548 // default server list.
1549 if (s.specified_servers.empty ())
1550 {
1a7f7b3f
DB
1551 if (! no_default)
1552 get_default_server_info (s, specified_servers);
2e15a955
DB
1553 }
1554 else
1555 {
1556 // Iterate over the specified servers. For each specification, add to
1557 // the list of servers.
1558 unsigned num_specified_servers = s.specified_servers.size ();
1559 for (unsigned i = 0; i < num_specified_servers; ++i)
1560 {
1561 const string &server = s.specified_servers[i];
1562 if (server.empty ())
1563 {
1564 // No server specified. Use the default servers.
1a7f7b3f
DB
1565 if (! no_default)
1566 get_default_server_info (s, specified_servers);
2e15a955
DB
1567 }
1568 else
1569 {
1570 // Work with the specified server
1571 compile_server_info server_info;
1f9938b9 1572
2e15a955
DB
1573 // See if a port was specified (:n suffix)
1574 vector<string> components;
1575 tokenize (server, components, ":");
1576 if (components.size () > 2)
1577 {
334df419
DB
1578 // Treat it as a certificate serial number. Look for
1579 // all known servers with this serial number.
0d1534e8
DB
1580 vector<compile_server_info> all_servers;
1581 get_all_server_info (s, all_servers);
2e15a955 1582
334df419
DB
1583 // Search the list of servers for ones matching the
1584 // serial number specified.
2e15a955 1585 unsigned found = 0;
0d1534e8
DB
1586 unsigned limit = all_servers.size ();
1587 for (unsigned j = 0; j < limit; ++j)
2e15a955 1588 {
334df419 1589 if (server == all_servers[j].certinfo)
2e15a955 1590 {
0d1534e8 1591 add_server_info (all_servers[j], specified_servers);
2e15a955
DB
1592 ++found;
1593 }
1594 }
334df419 1595 // Did we find one?
2e15a955 1596 if (s.verbose && found == 0)
334df419
DB
1597 cerr << "No server matching " << server << " found"
1598 << endl;
1599 } // specified by cert serial number
1600 else {
1601 // Not specified by serial number. Treat it as host name
1602 // and optional port number.
1603 if (components.size () == 2)
1604 {
1605 // Obtain the port number.
1606 const char *pstr = components.back ().c_str ();
1607 char *estr;
1608 errno = 0;
1609 unsigned long port = strtoul (pstr, & estr, 10);
1610 if (errno == 0 && *estr == '\0' && port <= USHRT_MAX)
1611 server_info.port = port;
1612 else
1613 {
1614 cerr << "Invalid port number specified: "
1615 << components.back ()
1616 << endl;
1617 continue;
1618 }
1619 }
1620
1621 // Obtain the host name.
1622 server_info.host_name = components.front ();
1623
1624 // Was a port specified?
1625 if (server_info.port != 0)
1626 {
1627 // A specific server was specified.
1628 // Resolve the server. It's not an error if it fails.
1629 // Just less info gathered.
1630 resolve_server (s, server_info);
1631 add_server_info (server_info, specified_servers);
1632 }
1633 else
1634 {
1635 // No port was specified, so find all known servers
1636 // on the specified host.
1637 resolve_host_name (s, server_info.host_name);
1638 vector<compile_server_info> all_servers;
1639 get_all_server_info (s, all_servers);
1640
1641 // Search the list of servers for ones matching the
1642 // one specified and obtain the port numbers.
1643 unsigned found = 0;
1644 unsigned limit = all_servers.size ();
1645 for (unsigned j = 0; j < limit; ++j)
1646 {
1647 if (server_info == all_servers[j])
1648 {
1649 add_server_info (all_servers[j], specified_servers);
1650 ++found;
1651 }
1652 }
1653 // Do we have a port number now?
1654 if (s.verbose && found == 0)
1655 cerr << "No server matching " << server << " found"
1656 << endl;
1657 } // No port specified
1658 } // Not specified by cert serial number
2e15a955
DB
1659 } // Specified server.
1660 } // Loop over --use-server options
1661 } // -- use-server specified
2e15a955
DB
1662 } // Server information is not cached
1663
c77af0d0 1664 // Add the information, but not duplicates.
2e15a955 1665 unsigned limit = specified_servers.size ();
c77af0d0 1666 for (unsigned i = 0; i < limit; ++i)
2e15a955 1667 add_server_info (specified_servers[i], servers);
1f9938b9
DB
1668}
1669
b0e5fa85 1670static void
0d1534e8 1671get_or_keep_trusted_server_info (
1f9938b9 1672 systemtap_session &s,
85007c04
DB
1673 vector<compile_server_info> &servers
1674)
1675{
0d1534e8
DB
1676 // We only need to obtain this once. This is a good thing(tm) since
1677 // obtaining this information is expensive.
1678 static vector<compile_server_info> trusted_servers;
1679 if (trusted_servers.empty ())
85007c04 1680 {
0d1534e8
DB
1681 // Maintain an empty entry to indicate that this search has been
1682 // performed, in case the search comes up empty.
1683 trusted_servers.push_back (compile_server_info ());
1684
1685#if HAVE_NSS
c77af0d0
DB
1686 // Check the private database first.
1687 string cert_db_path = private_ssl_cert_db_path (s);
1688 get_server_info_from_db (s, trusted_servers, cert_db_path);
0d1534e8 1689
c77af0d0
DB
1690 // Now check the global database.
1691 cert_db_path = global_ssl_cert_db_path ();
0d1534e8 1692 get_server_info_from_db (s, trusted_servers, cert_db_path);
0d1534e8
DB
1693#else // ! HAVE_NSS
1694 // Without NSS, we can't determine whether a server is trusted.
1695 // Issue a warning.
1696 if (s.verbose)
1697 clog << "Unable to determine server trust as an SSL peer" << endl;
1698#endif // ! HAVE_NSS
1699 } // Server information is not cached
1700
1701 if (! servers.empty ())
1702 {
1703 // Filter the existing vector by keeping the information in common with
1704 // the trusted_server vector.
1705 keep_common_server_info (trusted_servers, servers);
1706 }
1707 else
1708 {
c77af0d0 1709 // Add the information, but not duplicates.
0d1534e8 1710 unsigned limit = trusted_servers.size ();
c77af0d0 1711 for (unsigned i = 0; i < limit; ++i)
0d1534e8
DB
1712 add_server_info (trusted_servers[i], servers);
1713 }
1714}
1715
b0e5fa85 1716static void
0d1534e8
DB
1717get_or_keep_signing_server_info (
1718 systemtap_session &s,
1719 vector<compile_server_info> &servers
1720)
1721{
1722 // We only need to obtain this once. This is a good thing(tm) since
1723 // obtaining this information is expensive.
1724 static vector<compile_server_info> signing_servers;
1725 if (signing_servers.empty ())
1726 {
1727 // Maintain an empty entry to indicate that this search has been
1728 // performed, in case the search comes up empty.
1729 signing_servers.push_back (compile_server_info ());
1730
1731#if HAVE_NSS
0d1534e8 1732 // For all users, check the global database.
c77af0d0 1733 string cert_db_path = signing_cert_db_path ();
0d1534e8 1734 get_server_info_from_db (s, signing_servers, cert_db_path);
0d1534e8
DB
1735#else // ! HAVE_NSS
1736 // Without NSS, we can't determine whether a server is a trusted
1737 // signer. Issue a warning.
1738 if (s.verbose)
1739 clog << "Unable to determine server trust as a module signer" << endl;
1740#endif // ! HAVE_NSS
1741 } // Server information is not cached
1742
1743 if (! servers.empty ())
1744 {
1745 // Filter the existing vector by keeping the information in common with
1746 // the signing_server vector.
1747 keep_common_server_info (signing_servers, servers);
1748 }
1749 else
1750 {
c77af0d0 1751 // Add the information, but not duplicates.
0d1534e8 1752 unsigned limit = signing_servers.size ();
c77af0d0 1753 for (unsigned i = 0; i < limit; ++i)
0d1534e8
DB
1754 add_server_info (signing_servers[i], servers);
1755 }
1756}
1757
3d9dfde6 1758#if HAVE_NSS
0d1534e8 1759// Obtain information about servers from the certificates in the given database.
b0e5fa85 1760static void
0d1534e8
DB
1761get_server_info_from_db (
1762 systemtap_session &s,
1763 vector<compile_server_info> &servers,
1764 const string &cert_db_path
1765)
1766{
0d1534e8
DB
1767 // Make sure the given path exists.
1768 if (! file_exists (cert_db_path))
1769 {
1770 if (s.verbose > 1)
1771 cerr << "Certificate database '" << cert_db_path << "' does not exist."
1772 << endl;
1773 return;
1774 }
1775
1776 // Must predeclare these because of jumps to cleanup: below.
0d1534e8 1777 PK11SlotInfo *slot = NULL;
0d1534e8 1778 CERTCertDBHandle *handle;
c77af0d0
DB
1779 PRArenaPool *tmpArena = NULL;
1780 CERTCertList *certs = NULL;
1781 CERTCertificate *db_cert;
0d1534e8
DB
1782 vector<compile_server_info> temp_list;
1783
1784 // Initialize the NSS libraries -- readonly
1785 SECStatus secStatus = NSS_Init (cert_db_path.c_str ());
1786 if (secStatus != SECSuccess)
1787 {
1788 cerr << "Error initializing NSS for " << cert_db_path << endl;
1789 nssError ();
1790 goto cleanup;
1791 }
1792
1793 // Search the client-side database of trusted servers.
1794 slot = PK11_GetInternalKeySlot ();
1795 handle = CERT_GetDefaultCertDB();
c77af0d0
DB
1796 db_cert = PK11_FindCertFromNickname (server_cert_nickname, NULL);
1797 if (! db_cert)
0d1534e8
DB
1798 {
1799 // No trusted servers. Not an error. Just an empty list returned.
1800 goto cleanup;
1801 }
1802
1803 // Here, we have one cert with the desired nickname.
1804 // Now, we will attempt to get a list of ALL certs
1805 // with the same subject name as the cert we have. That list
1806 // should contain, at a minimum, the one cert we have already found.
1807 // If the list of certs is empty (NULL), the libraries have failed.
c77af0d0 1808 certs = CERT_CreateSubjectCertList (NULL, handle, & db_cert->derSubject,
0d1534e8 1809 PR_Now (), PR_FALSE);
c77af0d0 1810 CERT_DestroyCertificate (db_cert);
0d1534e8
DB
1811 if (! certs)
1812 {
1813 cerr << "Unable to query client certificate database: " << endl;
1814 PORT_SetError (SEC_ERROR_LIBRARY_FAILURE);
1815 nssError ();
1816 goto cleanup;
1817 }
1818
1819 // A memory pool to work in
1820 tmpArena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
1821 if (! tmpArena)
1822 {
1823 cerr << "Out of memory:";
1824 nssError ();
1825 goto cleanup;
1826 }
1827 for (CERTCertListNode *node = CERT_LIST_HEAD (certs);
1828 ! CERT_LIST_END (node, certs);
1829 node = CERT_LIST_NEXT (node))
1830 {
1831 compile_server_info server_info;
1832
1833 // The certificate we're working with.
c77af0d0 1834 db_cert = node->cert;
0d1534e8
DB
1835
1836 // Get the host name. It is in the alt-name extension of the
1837 // certificate.
1838 SECItem subAltName;
1839 subAltName.data = NULL;
c77af0d0 1840 secStatus = CERT_FindCertExtension (db_cert,
0d1534e8
DB
1841 SEC_OID_X509_SUBJECT_ALT_NAME,
1842 & subAltName);
1843 if (secStatus != SECSuccess || ! subAltName.data)
85007c04 1844 {
0d1534e8
DB
1845 cerr << "Unable to find alt name extension on server certificate: " << endl;
1846 nssError ();
85007c04
DB
1847 continue;
1848 }
85007c04 1849
0d1534e8
DB
1850 // Decode the extension.
1851 CERTGeneralName *nameList = CERT_DecodeAltNameExtension (tmpArena, & subAltName);
1852 SECITEM_FreeItem(& subAltName, PR_FALSE);
1853 if (! nameList)
85007c04 1854 {
0d1534e8
DB
1855 cerr << "Unable to decode alt name extension on server certificate: " << endl;
1856 nssError ();
85007c04
DB
1857 continue;
1858 }
0d1534e8
DB
1859
1860 // We're interested in the first alternate name.
1861 assert (nameList->type == certDNSName);
1862 server_info.host_name = string ((const char *)nameList->name.other.data,
1863 nameList->name.other.len);
1864 // Don't free nameList. It's part of the tmpArena.
1865
1866 // Get the serial number.
1867 ostringstream field;
334df419 1868 field << hex << setfill('0') << right;
c77af0d0 1869 for (unsigned i = 0; i < db_cert->serialNumber.len; ++i)
0d1534e8
DB
1870 {
1871 if (i > 0)
1872 field << ':';
334df419 1873 field << setw(2) << (unsigned)db_cert->serialNumber.data[i];
0d1534e8
DB
1874 }
1875 server_info.certinfo = field.str ();
1876
1877 // We can't call resolve_server while NSS is active on a database
1878 // since it may recursively call this function for another database.
1879 // So, keep a temporary list of the server info discovered and then
1880 // make a pass over it later resolving each server.
1881 add_server_info (server_info, temp_list);
1882 }
1883
1884 cleanup:
1885 if (certs)
1886 CERT_DestroyCertList (certs);
1887 if (slot)
1888 PK11_FreeSlot (slot);
1889 if (tmpArena)
1890 PORT_FreeArena (tmpArena, PR_FALSE);
1891
1892 NSS_Shutdown ();
1893
1894 // Resolve each server discovered in order to eliminate duplicates when
1895 // adding them to the target verctor.
1896 // This must be done after NSS_Shutdown since resolve_server can call this
1897 // function recursively.
1898 for (vector<compile_server_info>::iterator i = temp_list.begin ();
1899 i != temp_list.end ();
1900 ++i)
1901 {
1902 resolve_server (s, *i);
1903 add_server_info (*i, servers);
1904 }
0d1534e8 1905}
3d9dfde6 1906#endif // HAVE_NSS
0d1534e8 1907
b0e5fa85 1908static void
0d1534e8
DB
1909keep_compatible_server_info (
1910 systemtap_session &s,
1911 vector<compile_server_info> &servers
1912)
1913{
1914#if HAVE_AVAHI
1915 // Remove entries for servers incompatible with the host environment
1916 // from the given list of servers.
1917 // A compatible server compiles for the kernel release and architecture
1918 // of the host environment.
1919 for (unsigned i = 0; i < servers.size (); /**/)
1920 {
c77af0d0
DB
1921 // Retain empty entries.
1922 if (! servers[i].host_name.empty ())
85007c04 1923 {
c77af0d0
DB
1924 // Check the target of the server.
1925 if (servers[i].sysinfo != s.kernel_release + " " + s.architecture)
1926 {
1927 // Target platform mismatch.
1928 servers.erase (servers.begin () + i);
1929 continue;
1930 }
85007c04
DB
1931 }
1932
1933 // The server is compatible. Leave it in the list.
1934 ++i;
1935 }
0d1534e8
DB
1936#else // ! HAVE_AVAHI
1937 // Without Avahi, we can't obtain the target platform of the server.
1938 // Issue a warning.
1939 if (s.verbose)
1940 clog << "Unable to detect server compatibility" << endl;
1941 servers.clear ();
1942#endif
85007c04
DB
1943}
1944
0d1534e8
DB
1945// Attempt to obtain complete information about this server from the local
1946// name service and/or from online server discovery.
b0e5fa85
DB
1947static void
1948resolve_server (systemtap_session& s, compile_server_info &server_info)
0d1534e8
DB
1949{
1950 // Attempt to resolve the host name and ip.
1951 int status = resolve_host_name (s, server_info.host_name,
1952 & server_info.ip_address);
1953 if (status != 0)
b0e5fa85 1954 return; // message already issued.
0d1534e8
DB
1955
1956 // See if we can discover the target of this server.
1957 if (server_info.sysinfo.empty ())
1958 {
1959 vector<compile_server_info> online_servers;
1960 get_or_keep_online_server_info (s, online_servers);
1961 merge_server_info (online_servers, server_info);
1962 }
1963
1964 // See if we can discover the certificate of this server. Look in
1965 // the databases of trusted peers and trusted signers.
1966 if (server_info.certinfo.empty ())
1967 {
1968 vector<compile_server_info> trusted_servers;
1969 get_or_keep_trusted_server_info (s, trusted_servers);
1970 merge_server_info (trusted_servers, server_info);
1971 }
1972 if (server_info.certinfo.empty ())
1973 {
1974 vector<compile_server_info> signing_servers;
1975 get_or_keep_signing_server_info (s, signing_servers);
1976 merge_server_info (signing_servers, server_info);
1977 }
0d1534e8
DB
1978}
1979
1980// Obtain the canonical host name and, if requested, ip address of a host.
b0e5fa85
DB
1981static int
1982resolve_host_name (
0d1534e8
DB
1983 systemtap_session& s,
1984 string &host_name,
1985 string *ip_address
1986)
2e15a955
DB
1987{
1988 struct addrinfo hints;
1989 memset(& hints, 0, sizeof (hints));
1990 hints.ai_family = AF_INET; // AF_UNSPEC or AF_INET6 to force version
1991 struct addrinfo *res;
0d1534e8 1992 int status = getaddrinfo(host_name.c_str(), NULL, & hints, & res);
2e15a955
DB
1993 if (status != 0)
1994 goto error;
1995
1996 // Obtain the ip address and canonical name of the resolved server.
1997 assert (res);
1998 for (const struct addrinfo *p = res; p != NULL; p = p->ai_next)
1999 {
2000 if (p->ai_family != AF_INET)
2001 continue; // Not an IPv4 address
2002
0d1534e8 2003 // Get the canonical name.
2e15a955
DB
2004 char hbuf[NI_MAXHOST];
2005 status = getnameinfo (p->ai_addr, sizeof (*p->ai_addr),
2006 hbuf, sizeof (hbuf), NULL, 0,
2007 NI_NAMEREQD | NI_IDN);
2008 if (status != 0)
2009 continue;
0d1534e8
DB
2010 host_name = hbuf;
2011
2012 // Get the ip address, if requested
2013 if (ip_address)
2014 {
2015 // get the pointer to the address itself,
2016 struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr;
2017 void *addr = & ipv4->sin_addr;
2018
2019 // convert the IP to a string.
2020 char ipstr[INET_ADDRSTRLEN];
2021 inet_ntop(p->ai_family, addr, ipstr, sizeof (ipstr));
2022 *ip_address = ipstr;
2023 }
2e15a955 2024
2e15a955
DB
2025 break; // Use the info from the first IPv4 result.
2026 }
2027 freeaddrinfo(res); // free the linked list
2028
2029 if (status != 0)
2030 goto error;
2031
2032 return 0;
2033
2034 error:
0d1534e8 2035 if (s.verbose > 1)
2e15a955 2036 {
0d1534e8 2037 clog << "Unable to resolve host name " << host_name
2e15a955
DB
2038 << ": " << gai_strerror(status)
2039 << endl;
2040 }
0d1534e8 2041 return status;
2e15a955
DB
2042}
2043
85007c04 2044#if HAVE_AVAHI
131f914e
DB
2045// Avahi API Callbacks.
2046//-----------------------------------------------------------------------
85007c04
DB
2047struct browsing_context {
2048 AvahiSimplePoll *simple_poll;
2049 AvahiClient *client;
2050 vector<compile_server_info> *servers;
2051};
2052
0d1534e8
DB
2053static string
2054extract_field_from_avahi_txt (const string &label, const string &txt)
2055{
2056 // Extract the requested field from the Avahi TXT.
2057 string prefix = "\"" + label;
2058 size_t ix = txt.find (prefix);
2059 if (ix == string::npos)
2060 {
2061 // Label not found.
2062 return "";
2063 }
2064
2065 // This is the start of the field.
2066 string field = txt.substr (ix + prefix.size ());
2067
2068 // Find the end of the field.
2069 ix = field.find('"');
2070 if (ix != string::npos)
2071 field = field.substr (0, ix);
2072
2073 return field;
2074}
2075
85007c04
DB
2076extern "C"
2077void resolve_callback(
2078 AvahiServiceResolver *r,
2079 AVAHI_GCC_UNUSED AvahiIfIndex interface,
2080 AVAHI_GCC_UNUSED AvahiProtocol protocol,
2081 AvahiResolverEvent event,
2082 const char *name,
2083 const char *type,
2084 const char *domain,
2085 const char *host_name,
2086 const AvahiAddress *address,
2087 uint16_t port,
2088 AvahiStringList *txt,
2089 AvahiLookupResultFlags flags,
2090 AVAHI_GCC_UNUSED void* userdata) {
2091
2092 assert(r);
2093 const browsing_context *context = (browsing_context *)userdata;
2094 vector<compile_server_info> *servers = context->servers;
2095
2096 // Called whenever a service has been resolved successfully or timed out.
2097
2098 switch (event) {
2099 case AVAHI_RESOLVER_FAILURE:
2100 cerr << "Failed to resolve service '" << name
2101 << "' of type '" << type
2102 << "' in domain '" << domain
2103 << "': " << avahi_strerror(avahi_client_errno(avahi_service_resolver_get_client(r)))
2104 << endl;
2105 break;
2106
2107 case AVAHI_RESOLVER_FOUND: {
2108 char a[AVAHI_ADDRESS_STR_MAX], *t;
2109 avahi_address_snprint(a, sizeof(a), address);
2110 t = avahi_string_list_to_string(txt);
2111
2112 // Save the information of interest.
2113 compile_server_info info;
0d1534e8 2114 info.host_name = host_name;
85007c04
DB
2115 info.ip_address = strdup (a);
2116 info.port = port;
0d1534e8
DB
2117 info.sysinfo = extract_field_from_avahi_txt ("sysinfo=", t);
2118 info.certinfo = extract_field_from_avahi_txt ("certinfo=", t);
85007c04
DB
2119
2120 // Add this server to the list of discovered servers.
2e15a955 2121 add_server_info (info, *servers);
85007c04
DB
2122
2123 avahi_free(t);
2124 }
2125 }
2126
2127 avahi_service_resolver_free(r);
2128}
2129
2130extern "C"
2131void browse_callback(
2132 AvahiServiceBrowser *b,
2133 AvahiIfIndex interface,
2134 AvahiProtocol protocol,
2135 AvahiBrowserEvent event,
2136 const char *name,
2137 const char *type,
2138 const char *domain,
2139 AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
2140 void* userdata) {
2141
2142 browsing_context *context = (browsing_context *)userdata;
2143 AvahiClient *c = context->client;
2144 AvahiSimplePoll *simple_poll = context->simple_poll;
2145 assert(b);
2146
2147 // Called whenever a new services becomes available on the LAN or is removed from the LAN.
2148
2149 switch (event) {
2150 case AVAHI_BROWSER_FAILURE:
2151 cerr << "Avahi browse failed: "
2152 << avahi_strerror(avahi_client_errno(avahi_service_browser_get_client(b)))
2153 << endl;
2154 avahi_simple_poll_quit(simple_poll);
2155 break;
2156
2157 case AVAHI_BROWSER_NEW:
2158 // We ignore the returned resolver object. In the callback
2159 // function we free it. If the server is terminated before
2160 // the callback function is called the server will free
2161 // the resolver for us.
2162 if (!(avahi_service_resolver_new(c, interface, protocol, name, type, domain,
2163 AVAHI_PROTO_UNSPEC, (AvahiLookupFlags)0, resolve_callback, context))) {
2164 cerr << "Failed to resolve service '" << name
2165 << "': " << avahi_strerror(avahi_client_errno(c))
2166 << endl;
2167 }
2168 break;
2169
2170 case AVAHI_BROWSER_REMOVE:
2171 case AVAHI_BROWSER_ALL_FOR_NOW:
2172 case AVAHI_BROWSER_CACHE_EXHAUSTED:
2173 break;
2174 }
2175}
2176
2177extern "C"
2178void client_callback(AvahiClient *c, AvahiClientState state, AVAHI_GCC_UNUSED void * userdata) {
2179 assert(c);
2180 browsing_context *context = (browsing_context *)userdata;
2181 AvahiSimplePoll *simple_poll = context->simple_poll;
2182
2183 // Called whenever the client or server state changes.
2184
2185 if (state == AVAHI_CLIENT_FAILURE) {
2186 cerr << "Avahi Server connection failure: "
2187 << avahi_strerror(avahi_client_errno(c))
2188 << endl;
2189 avahi_simple_poll_quit(simple_poll);
2190 }
2191}
2192
2193extern "C"
2194void timeout_callback(AVAHI_GCC_UNUSED AvahiTimeout *e, AVAHI_GCC_UNUSED void *userdata) {
2195 browsing_context *context = (browsing_context *)userdata;
2196 AvahiSimplePoll *simple_poll = context->simple_poll;
2197 avahi_simple_poll_quit(simple_poll);
2198}
2199#endif // HAVE_AVAHI
2200
b0e5fa85 2201static void
0d1534e8
DB
2202get_or_keep_online_server_info (systemtap_session &s,
2203 vector<compile_server_info> &servers)
85007c04 2204{
2e15a955
DB
2205 // We only need to obtain this once. This is a good thing(tm) since
2206 // obtaining this information is expensive.
2207 static vector<compile_server_info> online_servers;
2208 if (online_servers.empty ())
2209 {
0d1534e8
DB
2210 // Maintain an empty entry to indicate that this search has been
2211 // performed, in case the search comes up empty.
2212 online_servers.push_back (compile_server_info ());
2213#if HAVE_AVAHI
2e15a955
DB
2214 // Must predeclare these due to jumping on error to fail:
2215 unsigned limit;
2216 vector<compile_server_info> raw_servers;
1f9938b9 2217
2e15a955
DB
2218 // Initialize.
2219 AvahiClient *client = NULL;
2220 AvahiServiceBrowser *sb = NULL;
85007c04 2221
2e15a955
DB
2222 // Allocate main loop object.
2223 AvahiSimplePoll *simple_poll;
2224 if (!(simple_poll = avahi_simple_poll_new()))
2225 {
2226 cerr << "Failed to create Avahi simple poll object" << endl;
2227 goto fail;
2228 }
2229 browsing_context context;
2230 context.simple_poll = simple_poll;
2231 context.servers = & raw_servers;
2232
2233 // Allocate a new Avahi client
2234 int error;
2235 client = avahi_client_new (avahi_simple_poll_get (simple_poll),
2236 (AvahiClientFlags)0,
2237 client_callback, & context, & error);
2238
2239 // Check whether creating the client object succeeded.
2240 if (! client)
2241 {
2242 cerr << "Failed to create Avahi client: "
2243 << avahi_strerror(error)
2244 << endl;
2245 goto fail;
2246 }
2247 context.client = client;
85007c04 2248
2e15a955
DB
2249 // Create the service browser.
2250 if (!(sb = avahi_service_browser_new (client, AVAHI_IF_UNSPEC,
2251 AVAHI_PROTO_UNSPEC, "_stap._tcp",
2252 NULL, (AvahiLookupFlags)0,
2253 browse_callback, & context)))
2254 {
2255 cerr << "Failed to create Avahi service browser: "
2256 << avahi_strerror(avahi_client_errno(client))
2257 << endl;
2258 goto fail;
2259 }
85007c04 2260
2e15a955
DB
2261 // Timeout after 2 seconds.
2262 struct timeval tv;
2263 avahi_simple_poll_get(simple_poll)->timeout_new(
85007c04 2264 avahi_simple_poll_get(simple_poll),
2e15a955
DB
2265 avahi_elapse_time(&tv, 1000*2, 0),
2266 timeout_callback,
2267 & context);
85007c04 2268
2e15a955
DB
2269 // Run the main loop.
2270 avahi_simple_poll_loop(simple_poll);
1f9938b9 2271
2e15a955
DB
2272 // Resolve each server discovered and eliminate duplicates.
2273 limit = raw_servers.size ();
2274 for (unsigned i = 0; i < limit; ++i)
2275 {
2276 compile_server_info &raw_server = raw_servers[i];
2277
2278 // Delete the domain, if it is '.local'
2279 string &host_name = raw_server.host_name;
2280 string::size_type dot_index = host_name.find ('.');
2281 assert (dot_index != 0);
2282 string domain = host_name.substr (dot_index + 1);
2283 if (domain == "local")
2284 host_name = host_name.substr (0, dot_index);
2285
2286 // Now resolve the server (name, ip address, etc)
2287 // Not an error if it fails. Just less info gathered.
2288 resolve_server (s, raw_server);
2289
2290 // Add it to the list of servers, unless it is duplicate.
2291 add_server_info (raw_server, online_servers);
2292 }
1f9938b9 2293
2e15a955
DB
2294 fail:
2295 // Cleanup.
2296 if (sb)
85007c04
DB
2297 avahi_service_browser_free(sb);
2298
2e15a955 2299 if (client)
85007c04
DB
2300 avahi_client_free(client);
2301
2e15a955 2302 if (simple_poll)
85007c04 2303 avahi_simple_poll_free(simple_poll);
0d1534e8
DB
2304#else // ! HAVE_AVAHI
2305 // Without Avahi, we can't detect online servers. Issue a warning.
2306 if (s.verbose)
2307 clog << "Unable to detect online servers" << endl;
2308#endif // ! HAVE_AVAHI
2e15a955
DB
2309 } // Server information is not cached.
2310
0d1534e8
DB
2311 if (! servers.empty ())
2312 {
2313 // Filter the existing vector by keeping the information in common with
2314 // the online_server vector.
2315 keep_common_server_info (online_servers, servers);
2316 }
2317 else
2318 {
c77af0d0 2319 // Add the information, but not duplicates.
0d1534e8 2320 unsigned limit = online_servers.size ();
c77af0d0 2321 for (unsigned i = 0; i < limit; ++i)
0d1534e8
DB
2322 add_server_info (online_servers[i], servers);
2323 }
85007c04 2324}
2e15a955 2325
0d1534e8
DB
2326// Add server info to a list, avoiding duplicates. Merge information from
2327// two duplicate items.
b0e5fa85 2328static void
2e15a955
DB
2329add_server_info (
2330 const compile_server_info &info, vector<compile_server_info>& list
2331)
2332{
0d1534e8
DB
2333 vector<compile_server_info>::iterator s =
2334 find (list.begin (), list.end (), info);
2335 if (s != list.end ())
2e15a955 2336 {
0d1534e8
DB
2337 // Duplicate. Merge the two items.
2338 merge_server_info (info, *s);
2339 return;
2e15a955
DB
2340 }
2341 list.push_back (info);
2342}
0d1534e8
DB
2343
2344// Filter the second vector by keeping information in common with the first
2345// vector.
b0e5fa85 2346static void
0d1534e8
DB
2347keep_common_server_info (
2348 const vector<compile_server_info> &info_to_keep,
2349 vector<compile_server_info> &filtered_info
2350)
2351{
2352 for (unsigned i = 0; i < filtered_info.size (); /**/)
2353 {
2354 const vector<compile_server_info>::const_iterator j =
2355 find (info_to_keep.begin (), info_to_keep.end (), filtered_info[i]);
2356 // Was the item found?
2357 if (j != info_to_keep.end ())
2358 {
2359 // This item was found in the info to be kept. Keep it and merge
2360 // the information.
2361 merge_server_info (*j, filtered_info[i]);
2362 ++i;
2363 continue;
2364 }
2365
2366 // The item was not found. Delete it.
2367 filtered_info.erase (filtered_info.begin () + i);
2368 }
2369}
2370
2371// Merge two compile server info items.
b0e5fa85 2372static void
0d1534e8
DB
2373merge_server_info (
2374 const compile_server_info &source,
2375 compile_server_info &target
2376)
2377{
2378 if (target.host_name.empty ())
2379 target.host_name = source.host_name;
2380 if (target.ip_address.empty ())
2381 target.ip_address = source.ip_address;
2382 if (target.port == 0)
2383 target.port = source.port;
2384 if (target.sysinfo.empty ())
2385 target.sysinfo = source.sysinfo;
2386 if (target.certinfo.empty ())
2387 target.certinfo = source.certinfo;
2388}
2389
2390// Merge two compile server info vectors.
b0e5fa85 2391static void
0d1534e8
DB
2392merge_server_info (
2393 const vector<compile_server_info> &source,
2394 vector<compile_server_info> &target
2395)
2396{
2397 for (vector<compile_server_info>::const_iterator i = source.begin ();
2398 i != source.end ();
2399 ++i)
2400 add_server_info (*i, target);
2401}
2402
2403// Merge info from a compile server info vector into a compile
2404// server info item.
b0e5fa85 2405static void
0d1534e8
DB
2406merge_server_info (
2407 const vector<compile_server_info> &source,
2408 compile_server_info &target
2409)
2410{
2411 for (vector<compile_server_info>::const_iterator i = source.begin ();
2412 i != source.end ();
2413 ++i)
2414 {
2415 if (*i == target)
2416 {
2417 merge_server_info (*i, target);
2418 break;
2419 }
2420 }
2421}
This page took 0.288033 seconds and 5 git commands to generate.