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