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