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