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