From 94b9fcc03560b36932405727759a5621966cd212 Mon Sep 17 00:00:00 2001 From: Jonathan Lebon Date: Fri, 13 Dec 2013 16:37:57 -0500 Subject: BZ1044429: fix client.exp and improve client/server --- csclient.cxx | 342 ++++++++++++---------- csclient.h | 4 +- nsscommon.cxx | 11 +- testsuite/lib/systemtap.exp | 1 + testsuite/systemtap.server/client.exp | 333 ++++++++++----------- testsuite/systemtap.server/server_args.exp | 17 +- testsuite/systemtap.server/server_concurrency.exp | 4 +- 7 files changed, 373 insertions(+), 339 deletions(-) diff --git a/csclient.cxx b/csclient.cxx index cce5428..568cc8f 100644 --- a/csclient.cxx +++ b/csclient.cxx @@ -1,6 +1,6 @@ /* Compile server client functions - Copyright (C) 2010-2013 Red Hat Inc. + Copyright (C) 2010-2014 Red Hat Inc. This file is part of systemtap, and is free software. You can redistribute it and/or modify it under the terms of the GNU General @@ -88,66 +88,71 @@ nsscommon_error (const char *msg, int logit __attribute ((unused))) // Information about compile servers. struct compile_server_info { - compile_server_info () + compile_server_info () : port(0), fully_specified(false) { memset (& address, 0, sizeof (address)); } string host_name; PRNetAddr address; + unsigned short port; + bool fully_specified; string version; string sysinfo; string certinfo; bool empty () const { - return this->host_name.empty () && ! this->hasAddress (); + return this->host_name.empty () && ! this->hasAddress () && certinfo.empty (); } bool hasAddress () const { return this->address.raw.family != 0; } - unsigned short port () const - { - if (this->address.raw.family == PR_AF_INET) - return ntohs (this->address.inet.port); - if (this->address.raw.family == PR_AF_INET6) - return ntohs (this->address.ipv6.port); - return 0; - } - unsigned short setPort (unsigned short port) + unsigned short setAddressPort (unsigned short port) { if (this->address.raw.family == PR_AF_INET) return this->address.inet.port = htons (port); if (this->address.raw.family == PR_AF_INET6) return this->address.ipv6.port = htons (port); + assert (0); return 0; } + bool isComplete () const + { + return this->hasAddress () && port != 0; + } bool operator== (const compile_server_info &that) const { - // If both ip addressed are not set, then the host names must match, otherwise - // the addresses must match. - if (! this->hasAddress() || ! that.hasAddress()) + // If one item or the other has only a name, and possibly a port specified, + // then allow a match by name and port only. This is so that the user can specify + // names which are returned by avahi, but are not dns resolvable. + // Otherwise, we will ignore the host_name. + if ((! this->hasAddress() && this->version.empty () && + this->sysinfo.empty () && this->certinfo.empty ()) || + (! that.hasAddress() && that.version.empty () && + that.sysinfo.empty () && that.certinfo.empty ())) { if (this->host_name != that.host_name) return false; } - else if (this->address != that.address) - return false; // Compare the other fields only if they have both been set. - if (this->port() != 0 && that.port() != 0 && - this->port() != that.port()) + if (this->hasAddress() && that.hasAddress() && + this->address != that.address) + return false; + if (this->port != 0 && that.port != 0 && + this->port != that.port) return false; if (! this->version.empty () && ! that.version.empty () && - this->version != that.version) + this->version != that.version) return false; if (! this->sysinfo.empty () && ! that.sysinfo.empty () && - this->sysinfo != that.sysinfo) + this->sysinfo != that.sysinfo) return false; if (! this->certinfo.empty () && ! that.certinfo.empty () && - this->certinfo != that.certinfo) + this->certinfo != that.certinfo) return false; return true; // They are equal @@ -179,6 +184,14 @@ preferred_order (vector &servers) sort (servers.begin (), servers.end ()); } +struct resolved_host +{ + string host_name; + PRNetAddr address; + resolved_host(string chost_name, PRNetAddr caddress): + host_name(chost_name), address(caddress) {} +}; + struct compile_server_cache { vector default_servers; @@ -187,7 +200,7 @@ struct compile_server_cache vector signing_servers; vector online_servers; vector all_servers; - map > resolved_servers; + map > resolved_hosts; }; // For filtering queries. @@ -234,7 +247,7 @@ static void resolve_host (systemtap_session& s, compile_server_info &server, vec // ----------------------------------------------------- // NSS related code used by the compile server client // ----------------------------------------------------- -static void add_server_trust (systemtap_session &s, const string &cert_db_path, const vector &server_list); +static void add_server_trust (systemtap_session &s, const string &cert_db_path, vector &server_list); static void revoke_server_trust (systemtap_session &s, const string &cert_db_path, const vector &server_list); static void get_server_info_from_db (systemtap_session &s, vector &servers, const string &cert_db_path); @@ -1132,10 +1145,10 @@ compile_server_client::find_and_connect_to_server () i != specified_servers.end (); ++i) { - // If we have an ip address and port number, then just use the one we've - // been given. Otherwise, check for matching online servers and try their + // If we have an ip address and were given a port number, then just use the one we've + // been given. Otherwise, check for matching compatible online servers and try their // ip addresses and ports. - if (i->hasAddress() && i->port() != 0) + if (i->hasAddress() && i->fully_specified) add_server_info (*i, server_list); else { @@ -1145,8 +1158,8 @@ compile_server_client::find_and_connect_to_server () // If no specific server (port) has been specified, // then we'll need the servers to be - // compatible and possible trusted as signers as well. - if (i->port() == 0) + // compatible and possibly trusted as signers as well. + if (! i->fully_specified) { get_or_keep_compatible_server_info (s, online_servers, true/*keep*/); if (! pr_contains (s.privilege, pr_stapdev)) @@ -1227,7 +1240,7 @@ compile_server_client::find_and_connect_to_server () int compile_server_client::compile_using_server ( - const vector &servers + vector &servers ) { // Make sure NSPR is initialized. Must be done before NSS is initialized @@ -1275,14 +1288,16 @@ compile_server_client::compile_using_server ( server_zipfile = s.tmpdir + "/server.zip"; // Try each server in turn. - for (vector::const_iterator j = servers.begin (); + for (vector::iterator j = servers.begin (); j != servers.end (); ++j) { // At a minimum we need an ip_address along with a port // number in order to contact the server. - if (! j->hasAddress() || j->port() == 0) + if (! j->hasAddress() || j->port == 0) continue; + // Set the port within the address. + j->setAddressPort (j->port); if (s.verbose >= 2) clog << _F("Attempting SSL connection with %s\n" @@ -1673,7 +1688,7 @@ static void add_server_trust ( systemtap_session &s, const string &cert_db_path, - const vector &server_list + vector &server_list ) { // Get a list of servers already trusted. This opens the database, so do it @@ -1716,7 +1731,7 @@ add_server_trust ( // Iterate over the servers to become trusted. Contact each one and // add it to the list of trusted servers if it is not already trusted. // client_connect will issue any error messages. - for (vector::const_iterator server = server_list.begin(); + for (vector::iterator server = server_list.begin(); server != server_list.end (); ++server) { @@ -1735,10 +1750,14 @@ add_server_trust ( trust_already_in_place (*server, server_list, cert_db_path, false/*revoking*/); continue; } + // At a minimum we need an ip_address along with a port // number in order to contact the server. - if (! server->hasAddress() || server->port() == 0) + if (! server->hasAddress() || server->port == 0) continue; + // Set the port within the address. + server->setAddressPort (server->port); + int rc = client_connect (*server, NULL, NULL, "permanent"); if (rc != SUCCESS) { @@ -2076,8 +2095,8 @@ ostream &operator<< (ostream &s, const compile_server_info &i) else s << "offline"; s << " port="; - if (i.port() != 0) - s << i.port(); + if (i.port != 0) + s << i.port; else s << "unknown"; s << " sysinfo=\""; @@ -2586,7 +2605,8 @@ isPort (const char *pstr, compile_server_info &server_info) clog << _F("Invalid port number specified: %s", pstr) << endl; return false; } - server_info.setPort (p); + server_info.port = p; + server_info.fully_specified = true; return true; } @@ -2665,8 +2685,10 @@ isIPv6 (const string &server, compile_server_info &server_info) if (! empty && components.size() != 8) return false; // Not a valid IPv6 address - // Calls to setPort and isPort need to know that this is an IPv6 address. - server_info.address.raw.family = PR_AF_INET6; + // Try to convert the string to an address. + PRStatus prStatus = PR_StringToNetAddr (ip.c_str(), & server_info.address); + if (prStatus != PR_SUCCESS) + return false; // Examine the optional port if (portIx != string::npos) @@ -2683,10 +2705,8 @@ isIPv6 (const string &server, compile_server_info &server_info) } } else - server_info.setPort (0); + server_info.port = 0; - // Treat the ip address string like a host name. - server_info.host_name = ip; return true; // valid IPv6 address. } @@ -2703,20 +2723,20 @@ isIPv4 (const string &server, compile_server_info &server_info) if (components.size() > 2) return false; // Not a valid IPv4 address - // Separate the host from the port (if any). - string host; + // Separate the address from the port (if any). + string addr; string port; if (components.size() <= 1) - host = server; + addr = server; else { - host = components[0]; + addr = components[0]; port = components[1]; } - // Separate the host components. + // Separate the address components. // There must be exactly 4 components. components.clear (); - tokenize (server, components, "."); + tokenize (addr, components, "."); if (components.size() != 4) return false; // Not a valid IPv4 address @@ -2732,8 +2752,10 @@ isIPv4 (const string &server, compile_server_info &server_info) return false; // Not a valid IPv4 address } - // Calls to setPort and isPort need to know that this is an IPv4 address. - server_info.address.raw.family = PR_AF_INET; + // Try to convert the string to an address. + PRStatus prStatus = PR_StringToNetAddr (addr.c_str(), & server_info.address); + if (prStatus != PR_SUCCESS) + return false; // Examine the optional port if (! port.empty ()) { @@ -2741,10 +2763,8 @@ isIPv4 (const string &server, compile_server_info &server_info) return false; // not a valid port } else - server_info.setPort (0); + server_info.port = 0; - // Treat the ip address string like a host name. - server_info.host_name = host; return true; // valid IPv4 address. } @@ -2754,8 +2774,6 @@ isCertSerialNumber (const string &server, compile_server_info &server_info) // This function assumes that we have already ruled out the server spec being an IPv6 address. // Certificate serial numbers are 5 fields separated by colons plus an optional 6th decimal // field specifying a port. - // Assume IPv4 (for now) when storing the port. - server_info.address.raw.family = PR_AF_INET; assert (! server.empty()); string host = server; vector components; @@ -2782,8 +2800,6 @@ isDomain (const string &server, compile_server_info &server_info) { // Accept one or two components separated by a colon. The first will be the domain name and // the second must a port number. - // Assume IPv4 (for now) when storing the port. - server_info.address.raw.family = PR_AF_INET; assert (! server.empty()); string host = server; vector components; @@ -2858,50 +2874,69 @@ get_specified_server_info ( // Check for IPv6 addresses first. It reduces the amount of checking necessary for // certificate serial numbers. compile_server_info server_info; - vector known_servers; + vector resolved_servers; if (isIPv6 (server, server_info) || isIPv4 (server, server_info) || - isDomain (server, server_info)) + isCertSerialNumber (server, server_info)) { - // Resolve this host and add any information that is discovered. - // Try to augment the resolved servers with information about known servers. - // There may be no intersection. - get_all_server_info (s, known_servers); - - vector resolved_servers; + // An address or cert serial number has been specified. + // No resolution is needed. + resolved_servers.push_back (server_info); + } + else if (isDomain (server, server_info)) + { + // Try to resolve the given name. resolve_host (s, server_info, resolved_servers); - - vector common_servers = resolved_servers; - keep_common_server_info (known_servers, common_servers); - if (! common_servers.empty ()) - add_server_info (common_servers, resolved_servers); - - if (s.verbose >= 3) - { - clog << _F("Servers matching %s: ", server.c_str()) << endl; - clog << resolved_servers; - } - add_server_info (resolved_servers, specified_servers); - } - else if (isCertSerialNumber (server, server_info)) - { - // The host could not be resolved. Try resolving it as a certificate serial - // number. Look for all known servers with this serial number and (optional) - // port number. - get_all_server_info (s, known_servers); - keep_server_info_with_cert_and_port (s, server_info, known_servers); - if (s.verbose >= 3) - { - clog << _F("Servers matching %s: ", server.c_str()) << endl; - clog << known_servers; - } - - add_server_info (known_servers, specified_servers); } else { clog << _F("Invalid server specification for --use-server: %s", server.c_str()) << endl; + continue; } + + // Now examine the server(s) identified and add them to the list of specified + // servers. + vector known_servers; + vector new_servers; + for (vector::iterator i = resolved_servers.begin(); + i != resolved_servers.end(); + ++i) + { + // If this item was fully specified, then just add it. + if (i->fully_specified) + add_server_info (*i, new_servers); + else { + // It was not fully specified, so we need additional info. Try + // to get it by matching what we have against other known servers. + if (known_servers.empty ()) + get_all_server_info (s, known_servers); + + // See if this server spec matches that of a known server + vector matched_servers = known_servers; + keep_common_server_info (*i, matched_servers); + + // If this server spec matches one or more known servers, then add the + // augmented info to the specified_servers. Otherwise, if this server + // spec is complete, then add it directly. Otherwise this server spec + // is incomplete. + if (! matched_servers.empty()) + add_server_info (matched_servers, new_servers); + else if (i->isComplete ()) + add_server_info (*i, new_servers); + else if (s.verbose >= 3) + clog << _("Incomplete server spec: ") << *i << endl; + } + } + + if (s.verbose >= 3) + { + clog << _F("Servers matching %s: ", server.c_str()) << endl; + clog << new_servers; + } + + // Add the newly identified servers to the list. + if (! new_servers.empty()) + add_server_info (new_servers, specified_servers); } // Loop over --use-server options } // -- use-server specified @@ -3087,13 +3122,16 @@ keep_server_info_with_cert_and_port ( continue; } if (servers[i].certinfo == server.certinfo && - (servers[i].port() == 0 || server.port() == 0 || - servers[i].port() == server.port())) + (servers[i].port == 0 || server.port == 0 || + servers[i].port == server.port)) { // If the server is not online, then use the specified // port, if any. - if (servers[i].port() == 0) - servers[i].setPort (server.port()); + if (servers[i].port == 0) + { + servers[i].port = server.port; + servers[i].fully_specified = server.fully_specified; + } ++i; continue; } @@ -3110,8 +3148,8 @@ resolve_host ( vector &resolved_servers ) { - vector& cached_servers = cscache(s)->resolved_servers[server.host_name]; - if (cached_servers.empty ()) + vector& cached_hosts = cscache(s)->resolved_hosts[server.host_name]; + if (cached_hosts.empty ()) { // The server's host_name member is a string containing either a host name or an ip address. // Either is acceptable for lookup. @@ -3128,10 +3166,8 @@ resolve_host ( // Failure to resolve will result in an appropriate message later, if other methods fail. if (rc != 0) { - // At a minimum, return the information we were given. if (s.verbose >= 6) clog << _F("%s not found: %s", lookup_name, gai_strerror (rc)) << endl; - add_server_info (server, cached_servers); } else { @@ -3139,28 +3175,23 @@ resolve_host ( assert (addr_info); for (const struct addrinfo *ai = addr_info; ai != NULL; ai = ai->ai_next) { - // Start with the info we were given. - compile_server_info new_server = server; + PRNetAddr new_address; // We support IPv4 and IPv6, Ignore other protocols, if (ai->ai_family == AF_INET) { // IPv4 Address struct sockaddr_in *ip = (struct sockaddr_in *)ai->ai_addr; - new_server.address.inet.family = PR_AF_INET; - if (ip->sin_port != 0) - new_server.address.inet.port = ip->sin_port; - new_server.address.inet.ip = ip->sin_addr.s_addr; + new_address.inet.family = PR_AF_INET; + new_address.inet.ip = ip->sin_addr.s_addr; } else if (ai->ai_family == AF_INET6) { // IPv6 Address struct sockaddr_in6 *ip = (struct sockaddr_in6 *)ai->ai_addr; - new_server.address.ipv6.family = PR_AF_INET6; - if (ip->sin6_port != 0) - new_server.address.ipv6.port = ip->sin6_port; - new_server.address.ipv6.scope_id = ip->sin6_scope_id; - copyAddress (new_server.address.ipv6.ip, ip->sin6_addr); + new_address.ipv6.family = PR_AF_INET6; + new_address.ipv6.scope_id = ip->sin6_scope_id; + copyAddress (new_address.ipv6.ip, ip->sin6_addr); } else continue; @@ -3169,25 +3200,54 @@ resolve_host ( char hbuf[NI_MAXHOST]; int status = getnameinfo (ai->ai_addr, ai->ai_addrlen, hbuf, sizeof (hbuf), NULL, 0, NI_NAMEREQD | NI_IDN); - if (status == 0) - new_server.host_name = hbuf; + if (status != 0) + hbuf[0] = '\0'; - // Add the new resolved server to the list. - add_server_info (new_server, cached_servers); + resolved_host *new_host = new resolved_host(hbuf, new_address); + cached_hosts.push_back(*new_host); } } if (addr_info) freeaddrinfo (addr_info); // free the linked list - - if (s.verbose >= 6) - { - clog << _F("%s resolves to:", lookup_name) << endl; - clog << cached_servers; - } } - // Add the information, but not duplicates. - add_server_info (cached_servers, resolved_servers); + // If no addresses were resolved, then return the info we were given. + if (cached_hosts.empty()) + add_server_info (server, resolved_servers); + else { + // We will add a new server for each address resolved + vector new_servers; + for (vector::const_iterator it = cached_hosts.begin(); + it != cached_hosts.end(); ++it) + { + // Start with the info we were given + compile_server_info new_server = server; + + // NB: do not overwrite port info + if (it->address.raw.family == AF_INET) + { + new_server.address.inet.family = PR_AF_INET; + new_server.address.inet.ip = it->address.inet.ip; + } + else // AF_INET6 + { + new_server.address.ipv6.family = PR_AF_INET6; + new_server.address.ipv6.scope_id = it->address.ipv6.scope_id; + new_server.address.ipv6.ip = it->address.ipv6.ip; + } + if (!it->host_name.empty()) + new_server.host_name = it->host_name; + add_server_info (new_server, new_servers); + } + + if (s.verbose >= 6) + { + clog << _F("%s resolves to:", server.host_name.c_str()) << endl; + clog << new_servers; + } + + add_server_info (new_servers, resolved_servers); + } } #if HAVE_AVAHI @@ -3268,12 +3328,12 @@ void resolve_callback( // We support both IPv4 and IPv6. Ignore other protocols. if (protocol == AVAHI_PROTO_INET6) { info.address.ipv6.family = PR_AF_INET6; - info.address.ipv6.port = htons (port); info.address.ipv6.scope_id = interface; + info.port = port; } else if (protocol == AVAHI_PROTO_INET) { info.address.inet.family = PR_AF_INET; - info.address.inet.port = htons (port); + info.port = port; } else break; @@ -3388,7 +3448,6 @@ get_or_keep_online_server_info ( online_servers.push_back (compile_server_info ()); #if HAVE_AVAHI // Must predeclare these due to jumping on error to fail: - unsigned limit; vector avahi_servers; // Initialize. @@ -3449,25 +3508,6 @@ get_or_keep_online_server_info ( clog << avahi_servers; } - // Resolve each server discovered, in case there are alternate ways to reach them - // (e.g. localhost). - limit = avahi_servers.size (); - for (unsigned i = 0; i < limit; ++i) - { - compile_server_info &avahi_server = avahi_servers[i]; - - // Delete the domain, if it is '.local' - string &host_name = avahi_server.host_name; - string::size_type dot_index = host_name.find ('.'); - assert (dot_index != 0); - string domain = host_name.substr (dot_index + 1); - if (domain == "local") - host_name = host_name.substr (0, dot_index); - - // Add it to the list of servers, unless it is duplicate. - resolve_host (s, avahi_server, online_servers); - } - // Merge with the list of servers, as obtained by avahi. add_server_info (avahi_servers, online_servers); @@ -3619,15 +3659,19 @@ merge_server_info ( compile_server_info &target ) { - if (target.host_name.empty ()) + // Copy the host name if the source has one. + if (! source.host_name.empty()) target.host_name = source.host_name; // Copy the address unconditionally, if the source has an address, even if they are already // equal. The source address may be an IPv6 address with a scope_id that the target is missing. assert (! target.hasAddress () || ! source.hasAddress () || source.address == target.address); if (source.hasAddress ()) copyNetAddr (target.address, source.address); - if (target.port() == 0) - target.setPort (source.port()); + if (target.port == 0) + { + target.port = source.port; + target.fully_specified = source.fully_specified; + } if (target.sysinfo.empty ()) target.sysinfo = source.sysinfo; if (target.version.empty ()) diff --git a/csclient.h b/csclient.h index b7eeda4..e4508ea 100644 --- a/csclient.h +++ b/csclient.h @@ -1,5 +1,5 @@ // -*- C++ -*- -// Copyright (C) 2010-2011 Red Hat Inc. +// Copyright (C) 2010-2014 Red Hat Inc. // // This file is part of systemtap, and is free software. You can // redistribute it and/or modify it under the terms of the GNU General @@ -35,7 +35,7 @@ private: ); int add_package_args (); int add_package_arg (const std::string &arg); - int compile_using_server (const std::vector &servers); + int compile_using_server (std::vector &servers); int add_localization_variables(); int read_from_file (const std::string &fname, int &data); diff --git a/nsscommon.cxx b/nsscommon.cxx index 70aac54..78e38b0 100644 --- a/nsscommon.cxx +++ b/nsscommon.cxx @@ -1,7 +1,7 @@ /* Common functions used by the NSS-aware code in systemtap. - Copyright (C) 2009-2013 Red Hat Inc. + Copyright (C) 2009-2014 Red Hat Inc. This file is part of systemtap, and is free software. You can redistribute it and/or modify it under the terms of the GNU General Public @@ -998,12 +998,19 @@ gen_cert_db (const string &db_path, const string &extraDnsNames, bool use_passwo goto error; } - // Now, generate the cert. We need our host name and the supplied additional dns names (if any). + // For the cert, we need our host name. struct utsname utsname; uname (& utsname); dnsNames = utsname.nodename; + + // Because avahi identifies hosts using a ".local" domain, add one to the list of names. + dnsNames += string(",") + dnsNames + ".local"; + + // Add any extra names that were supplied. if (! extraDnsNames.empty ()) dnsNames += "," + extraDnsNames; + + // Now, generate the cert. cert = create_cert (cr, dnsNames); CERT_DestroyCertificateRequest (cr); if (! cert) diff --git a/testsuite/lib/systemtap.exp b/testsuite/lib/systemtap.exp index d4e4d85..64bbed1 100644 --- a/testsuite/lib/systemtap.exp +++ b/testsuite/lib/systemtap.exp @@ -244,6 +244,7 @@ proc setup_server { args } { } # Make sure that stap can find the server. + exec sleep 1 set use_server --use-server set res [catch { exec stap --list-servers=online,trusted,compatible >& stap-list-servers.out } looksee] verbose -log "stap --list-servers returned: res==$res" diff --git a/testsuite/systemtap.server/client.exp b/testsuite/systemtap.server/client.exp index f0d0728..41a06cc 100644 --- a/testsuite/systemtap.server/client.exp +++ b/testsuite/systemtap.server/client.exp @@ -1,3 +1,6 @@ +# In this test, we are using arrays as sets. The key itself does not matter, +# only the value + # many of these tests use as_root if {! [installtest_p]} { return } if {! [nss_p]} { return } @@ -5,37 +8,111 @@ if {! [nss_p]} { return } # Let's start with a clean slate in terms of trust. exec rm -fr $env(SYSTEMTAP_DIR)/ssl -# Compare two arrays. If equal, return 1, otherwise 0. -# (Borrowed from http://wiki.tcl.tk/1032.) -proc array_compare {array1 array2} { - upvar 1 $array1 foo $array2 bar +# arr given as list (e.g. [array_has [array get myarr] elem]) +proc array_has {arr elem} { - if {![array exists foo]} { - return -code error "$array1 is not an array" - } - if {![array exists bar]} { - return -code error "$array2 is not an array" - } - if {[array size foo] != [array size bar]} { - return 0 - } + array set foo $arr if {[array size foo] == 0} { + return 0 + } + + foreach {key val} [array get foo] { + if {[string equal $val $elem]} { + return 1 + } + } + + return 0 +} + +# Check if array1 is a subset of array2. Returns 1 if yes, otherwise 0. +# (Modified from http://wiki.tcl.tk/1032.) +proc array_in {array1 array2} { + upvar 1 $array1 sub $array2 super + + if {![array exists sub]} { + return -code error "$array1 is not an array" + } + if {![array exists super]} { + return -code error "$array2 is not an array" + } + if {[array size sub] > [array size super]} { + return 0 + } + if {[array size sub] == 0} { return 1 } - set keys [lsort -unique [concat [array names foo] [array names bar]]] - if {[llength $keys] != [array size foo]} { - return 0 - } - - foreach key $keys { - if {$foo($key) ne $bar($key)} { + foreach key [array names sub] { + if {![array_has [array get super] $sub($key)]} { return 0 } } return 1 } +# Check if array1 and array2 have all the same elements +proc array_equal {array1 array2} { + upvar 1 $array1 foo $array2 bar + return [array_in foo bar] \ + && [array_in bar foo] +} + +# Returns the union of the three arrays +proc array_union {array1 array2 array3} { + upvar 1 $array1 foo $array2 bar $array3 baz + array unset ::union + + if {![array exists foo]} { + return -code error "$array1 is not an array" + } + if {![array exists bar]} { + return -code error "$array2 is not an array" + } + if {![array exists baz]} { + return -code error "$array3 is not an array" + } + + set n 0 + foreach key [array names foo] { + set ::union($n) $foo($key) + incr n + } + foreach key [array names bar] { + if {![array_has [array get ::union] $bar($key)]} { + set ::union($n) $bar($key) + incr n + } + } + foreach key [array names baz] { + if {![array_has [array get ::union] $baz($key)]} { + set ::union($n) $baz($key) + incr n + } + } +} + +# Returns all the elements in array_new not in array_old +proc array_diff {array_new array_old} { + upvar 1 $array_new anew $array_old aold + array unset ::diff + + if {![array exists anew]} { + return -code error "$array_new is not an array" + } + if {![array exists aold]} { + return -code error "$array_old is not an array" + } + + set n 0 + foreach key [array names anew] { + if {![array_has [array get aold] $anew($key)]} { + set ::diff($n) $anew($key) + incr n + } + } +} + # Test the --list-servers option and return an array of the servers found. proc list_servers { TEST_NAME SERVER_SPEC args } { set failed 0 @@ -82,7 +159,6 @@ proc list_servers { TEST_NAME SERVER_SPEC args } { # Sometimes, we'll see a server running from the last test, if it # hasn't quite died yet. So, make sure we get the same result twice. list_servers "List existing online servers" online -array unset eos1 array set existing_online_servers [array get servers] set i 0 while {1} { @@ -94,9 +170,9 @@ while {1} { fail "List existing online servers: never got stable" return } - + verbose -log "verify existing online servers - attempt $i: [array size existing_online_servers] [array size eos2]" - if {[array_compare existing_online_servers eos2]} { + if {[array_equal existing_online_servers eos2]} { # Arrays are equal, we're done break } @@ -120,37 +196,23 @@ list_servers "List all existing servers" all array unset all_existing_servers array set all_existing_servers [array get servers] -set test "Verify existing online server list" -if {[array_compare existing_online_servers all_existing_servers]} { - pass "$test" -} else { - fail "$test" -} +# First we create a union +array_union existing_online_servers \ + existing_trusted_servers \ + existing_signing_servers +array set existing_unioned_servers [array get union] -set test "Verify existing trusted server list" -if {[array_compare existing_trusted_servers all_existing_servers]} { - pass "$test" -} else { - fail "$test" -} - -set test "Verify existing signing server list" -if {[array_compare existing_signing_servers all_existing_servers]} { - pass "$test" -} else { - fail "$test" -} - -set test "Verify all existing server list" -if {[array_compare existing_online_servers all_existing_servers]} { +# Now we can compare +set test "Verify existing server list" +if {[array_equal existing_unioned_servers all_existing_servers]} { pass "$test" } else { fail "$test" } list_servers "List existing online servers (before start)" online -array unset existing_online_servers1 -array set existing_online_servers1 [array get servers] +array unset existing_online_servers +array set existing_online_servers [array get servers] # Now start our own server and make sure we can work with it. if {! [setup_server] || $avahi_ok_p != 1} { @@ -159,31 +221,22 @@ if {! [setup_server] || $avahi_ok_p != 1} { } # Our server should now appear online, separate from the previously discovered -# online servers. Note that our server could generate serveral listings -# because it could appear at more than one ip address, +# online servers. Note that our server could generate serveral listings because +# it could appear at more than one ip address, list_servers "List current online servers" online array unset current_online_servers array set current_online_servers [array get servers] +# array_diff will give us all the servers in current not in existing +array_diff current_online_servers existing_online_servers +array unset new_online_servers +array set new_online_servers [array get diff] + set test "New online servers" -set n 0 -foreach idx1 [array names current_online_servers] { - set found 0 - foreach idx2 [array names existing_online_servers] { - if {"$existing_online_servers($idx2)" == "$current_online_servers($idx1)"} { - set found 1 - break - } - } - if {$found == 0} { - set new_online_servers($n) "$current_online_servers($idx1)" - incr n - } -} -if {$n == 0} { - fail "$test" -} else { +if {[array size new_online_servers] > 0} { pass "$test" +} else { + fail "$test" } # Our server should now be trusted, separate from the previously discovered @@ -192,48 +245,27 @@ list_servers "List current trusted servers" online,trusted array unset current_trusted_servers array set current_trusted_servers [array get servers] +# array_diff will give us all the servers in current not in existing +array_diff current_trusted_servers existing_trusted_servers +array unset new_trusted_servers +array set new_trusted_servers [array get diff] + set test "New trusted servers" -set n 0 -foreach idx1 [array names current_trusted_servers] { - set found 0 - foreach idx2 [array names existing_trusted_servers] { - if {"$existing_trusted_servers($idx2)" == "$current_trusted_servers($idx1)"} { - set found 1 - break - } - } - if {$found == 0} { - set new_trusted_servers($n) "$current_trusted_servers($idx1)" - incr n - } -} -if {$n == 0} { - fail "$test" -} else { +if {[array size new_trusted_servers] > 0} { pass "$test" +} else { + fail "$test" } # The new servers should automatically be trusted, so the new_trusted_servers -# array should be a subset of the new_online_servers -# array, but not necessarilty vice-versa, since new servers may have come -# online independently of our testing. +# array should be a subset of the new_online_servers array, but not necessarily +# vice-versa, since new servers may have come online independently of our +# testing. set test "Verify new trusted server list" -set failed 0 -foreach idx1 [array names new_trusted_servers] { - set found 0 - foreach idx2 [array names new_online_servers] { - if {"$new_trusted_servers($idx1)" == "$new_online_servers($idx2)"} { - set found 1 - break - } - } - if {$found == 0} { - set failed 1 - fail "$test $idx1" - } -} -if {$failed == 0} { +if {[array_in new_trusted_servers new_online_servers]} { pass "$test" +} else { + fail "$test" } # The newly trusted servers represent the server we just started. @@ -247,7 +279,7 @@ array unset current_signing_servers array set current_signing_servers [array get servers] set test "No new signing servers" -if {[array_compare current_signing_servers existing_signing_servers]} { +if {[array_equal current_signing_servers existing_signing_servers]} { pass "$test" } else { fail "$test" @@ -287,7 +319,7 @@ array unset current_trusted_servers array set current_trusted_servers [array get servers] set test "No longer trusted after revokation by host name" -if {[array_compare current_trusted_servers existing_trusted_servers]} { +if {[array_equal current_trusted_servers existing_trusted_servers]} { pass "$test" } else { fail "$test" @@ -320,54 +352,24 @@ list_servers "List current trusted servers after reinstatement by ip address" on array unset current_trusted_servers array set current_trusted_servers [array get servers] +array_diff current_trusted_servers existing_trusted_servers +array unset new_trusted_servers +array set new_trusted_servers [array get diff] + set test "New trusted servers after reinstatement by ip address" -array unset new_trusted_servers -set n 0 -foreach idx1 [array names current_trusted_servers] { - set found 0 - foreach idx2 [array names existing_trusted_servers] { - if {"$existing_trusted_servers($idx2)" == "$current_trusted_servers($idx1)"} { - set found 1 - break - } - } - if {$found == 0} { - set new_trusted_servers($n) "$current_trusted_servers($idx1)" - incr n - } -} -if {$n == 0} { - fail "$test" -} else { +if {[array size new_trusted_servers] > 0} { pass "$test" +} else { + fail "$test" } # The new_trusted_servers array should now match the our_servers array, since # the our_servers array is a copy of the original new_trusted_servers array. -set test "Number of new trusted servers matches after reinstatement by ip address" -if {[array size new_trusted_servers] == [array size our_servers]} { - pass "$test" -} else { - fail "$test" -} set test "New trusted servers matches after reinstatement by ip address" -set n 0 -foreach idx1 [array names new_trusted_servers] { - set found 0 - foreach idx2 [array names our_servers] { - if {"$our_servers($idx2)" == "$new_trusted_servers($idx1)"} { - set found 1 - break - } - } - if {$found == 1} { - incr n - } -} -if {$n != [array size new_trusted_servers]} { - fail "$test" -} else { +if {[array_equal new_trusted_servers our_servers]} { pass "$test" +} else { + fail "$test" } # Trust our server as a module signer. This must be done as root. Specify @@ -394,28 +396,18 @@ list_servers "List current online signing servers" online,signer array unset current_signing_servers array set current_signing_servers [array get servers] +array_diff current_signing_servers existing_signing_servers +array unset new_signing_servers +array set new_signing_servers [array get diff] + set test "New signing servers" -set n 0 -foreach idx1 [array names current_signing_servers] { - set found 0 - foreach idx2 [array names existing_signing_servers] { - if {"$existing_signing_servers($idx2)" == "$current_signing_servers($idx1)"} { - set found 1 - break - } - } - if {$found == 0} { - set new_signing_servers($n) "$current_signing_servers($idx1)" - incr n - } -} if {$effective_pid == 0} { setup_xfail *-*-* } -if {$n == 0} { - fail "$test" -} else { +if {[array size new_signing_servers] > 0} { pass "$test" +} else { + fail "$test" } # The new_signing_servers array should now match the our_servers array, since @@ -428,26 +420,13 @@ if {$effective_pid == 0} { if {[array size new_signing_servers] == [array size our_servers]} { pass "$test" set test "New signing servers matches" - set n 0 - foreach idx1 [array names new_signing_servers] { - set found 0 - foreach idx2 [array names our_servers] { - if {"$our_servers($idx2)" == "$new_signing_servers($idx1)"} { - set found 1 - break - } - } - if {$found == 1} { - incr n - } - } if {$effective_pid == 0} { setup_xfail *-*-* } - if {$n != [array size new_signing_servers]} { - fail "$test" - } else { + if {[array_equal new_signing_servers our_servers]} { pass "$test" + } else { + fail "$test" } } else { fail "$test" @@ -594,7 +573,7 @@ array unset current_signing_servers array set current_signing_servers [array get servers] set test "No longer trusted as a signer after revokation" -if {[array_compare current_signing_servers existing_signing_servers]} { +if {[array_in current_signing_servers existing_signing_servers]} { pass "$test" } else { fail "$test" diff --git a/testsuite/systemtap.server/server_args.exp b/testsuite/systemtap.server/server_args.exp index 2f5deed..91536a5 100644 --- a/testsuite/systemtap.server/server_args.exp +++ b/testsuite/systemtap.server/server_args.exp @@ -66,20 +66,23 @@ proc stap_direct_and_with_client {stap options} { verbose -log $res_stap_client # Now check the output - set skip 0 + set skip_hostname_mode 0 set n 0 set expected [split $res_stap "\n"] set received [split $res_stap_client "\n"] foreach line $received { - # Instructed to skip a line? - if {$skip} { - set skip [expr $skip - 1] - verbose -log "skipping: $line" - continue + # Instructed to skip hostnames? + if {$skip_hostname_mode} { + if {[regexp {^ \S+$} $line]} { + verbose -log "skipping: $line" + continue + } else { + set skip_hostname_mode 0 + } } # Ignore warnings about the domain name on the certificate not matching if {[regexp {^WARNING: The domain name, [^,]*, does not match the DNS name\(s\) on the server certificate:} $line]} { - set skip 1 + set skip_hostname_mode 1 verbose -log "skipping: $line" continue } diff --git a/testsuite/systemtap.server/server_concurrency.exp b/testsuite/systemtap.server/server_concurrency.exp index dbacb51..a31415b 100644 --- a/testsuite/systemtap.server/server_concurrency.exp +++ b/testsuite/systemtap.server/server_concurrency.exp @@ -26,7 +26,7 @@ if {! [setup_server --max-threads 6]} { set server_port 0 set f [open $logfile] set matched 0 -verbose -log "Server ouput: " +verbose -log "Server output: " while {1} { set line [gets $f] if {[eof $f]} { @@ -34,7 +34,7 @@ while {1} { break } verbose -log "$line" - if { [regexp {^.*Using network port (\d*)$} $line matched server_port ] } { + if { [regexp {^.*Using network address .+:(\d+)$} $line matched server_port ] } { close $f break } -- 1.8.3.1