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