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