LCOV - code coverage report
Current view: top level - mnt/wasteland/wcohen/systemtap_write/systemtap - stap-serverd.cxx (source / functions) Hit Total Coverage
Test: stap.info Lines: 548 819 66.9 %
Date: 2013-03-08 Functions: 31 35 88.6 %
Branches: 600 2030 29.6 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :   SSL server program listens on a port, accepts client connection, reads
       3                 :            :   the data into a temporary file, calls the systemtap translator and
       4                 :            :   then transmits the resulting file back to the client.
       5                 :            : 
       6                 :            :   Copyright (C) 2011-2012 Red Hat Inc.
       7                 :            : 
       8                 :            :   This file is part of systemtap, and is free software.  You can
       9                 :            :   redistribute it and/or modify it under the terms of the GNU General Public
      10                 :            :   License as published by the Free Software Foundation; either version 2 of the
      11                 :            :   License, or (at your option) any later version.
      12                 :            : 
      13                 :            :   This program is distributed in the hope that it will be useful,
      14                 :            :   but WITHOUT ANY WARRANTY; without even the implied warranty of
      15                 :            :   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      16                 :            :   GNU General Public License for more details.
      17                 :            : 
      18                 :            :   You should have received a copy of the GNU General Public License
      19                 :            :   along with this program.  If not, see <http://www.gnu.org/licenses/>.
      20                 :            : */
      21                 :            : #include "config.h"
      22                 :            : 
      23                 :            : #include <fstream>
      24                 :            : #include <string>
      25                 :            : #include <cerrno>
      26                 :            : #include <cassert>
      27                 :            : #include <climits>
      28                 :            : #include <iostream>
      29                 :            : #include <map>
      30                 :            : 
      31                 :            : extern "C" {
      32                 :            : #include <unistd.h>
      33                 :            : #include <getopt.h>
      34                 :            : #include <wordexp.h>
      35                 :            : #include <glob.h>
      36                 :            : #include <fcntl.h>
      37                 :            : #include <sys/stat.h>
      38                 :            : #include <sys/utsname.h>
      39                 :            : #include <sys/types.h>
      40                 :            : #include <pwd.h>
      41                 :            : #include <semaphore.h>
      42                 :            : 
      43                 :            : #include <nspr.h>
      44                 :            : #include <ssl.h>
      45                 :            : #include <nss.h>
      46                 :            : #include <keyhi.h>
      47                 :            : #include <regex.h>
      48                 :            : 
      49                 :            : #if HAVE_AVAHI
      50                 :            : #include <avahi-client/publish.h>
      51                 :            : #include <avahi-common/alternative.h>
      52                 :            : #include <avahi-common/thread-watch.h>
      53                 :            : #include <avahi-common/malloc.h>
      54                 :            : #include <avahi-common/error.h>
      55                 :            : #endif
      56                 :            : }
      57                 :            : 
      58                 :            : #include "util.h"
      59                 :            : #include "nsscommon.h"
      60                 :            : #include "cscommon.h"
      61                 :            : #include "cmdline.h"
      62                 :            : 
      63                 :            : using namespace std;
      64                 :            : 
      65                 :            : static void cleanup ();
      66                 :            : static PRStatus spawn_and_wait (const vector<string> &argv,
      67                 :            :                                 const char* fd0, const char* fd1, const char* fd2,
      68                 :            :                                 const char *pwd, const vector<string>& envVec = vector<string> ());
      69                 :            : 
      70                 :            : /* getopt variables */
      71                 :            : extern int optind;
      72                 :            : 
      73                 :            : /* File scope statics. Set during argument parsing and initialization. */
      74                 :            : static bool use_db_password;
      75                 :            : static unsigned short port;
      76                 :            : static long max_threads;
      77                 :          5 : static string cert_db_path;
      78                 :          5 : static string stap_options;
      79                 :          5 : static string uname_r;
      80                 :          5 : static string arch;
      81                 :          5 : static string cert_serial_number;
      82                 :          5 : static string B_options;
      83                 :          5 : static string I_options;
      84                 :          5 : static string R_option;
      85                 :          5 : static string D_options;
      86                 :            : static bool   keep_temp;
      87                 :            : 
      88                 :            : sem_t sem_client;
      89                 :            : static int pending_interrupts;
      90                 :            : #define CONCURRENCY_TIMEOUT_S 3
      91                 :            : 
      92                 :            : // Message handling.
      93                 :            : // Server_error messages are printed to stderr and logged, if requested.
      94                 :            : static void
      95                 :          5 : server_error (const string &msg, int logit = true)
      96                 :            : {
      97                 :          5 :   cerr << msg << endl << flush;
      98                 :            :   // Log it, but avoid repeated messages to the terminal.
      99 [ +  - ][ +  - ]:          5 :   if (logit && log_ok ())
                 [ +  - ]
     100                 :          5 :     log (msg);
     101                 :          5 : }
     102                 :            : 
     103                 :            : // client_error messages are treated as server errors and also printed to the client's stderr.
     104                 :            : static void
     105                 :          2 : client_error (const string &msg, string stapstderr)
     106                 :            : {
     107                 :          2 :   server_error (msg);
     108         [ +  - ]:          2 :   if (! stapstderr.empty ())
     109                 :            :     {
     110         [ +  - ]:          2 :       ofstream errfile;
     111 [ +  - ][ +  - ]:          2 :       errfile.open (stapstderr.c_str (), ios_base::app);
     112 [ +  - ][ -  + ]:          2 :       if (! errfile.good ())
     113                 :          0 :         server_error (_F("Could not open client stderr file %s: %s", stapstderr.c_str (),
     114   [ #  #  #  # ]:          0 :                          strerror (errno)));
         [ #  # ][ #  # ]
     115                 :            :       else
     116 [ +  - ][ +  - ]:          2 :         errfile << "Server: " << msg << endl;
         [ +  - ][ +  - ]
     117                 :            :       // NB: No need to close errfile
     118                 :            :     }
     119                 :          2 : }
     120                 :            : 
     121                 :            : // Messages from the nss common code are treated as server errors.
     122                 :            : extern "C"
     123                 :            : void
     124                 :          0 : nsscommon_error (const char *msg, int logit)
     125                 :            : {
     126 [ #  # ][ #  # ]:          0 :   server_error (msg, logit);
                 [ #  # ]
     127                 :          0 : }
     128                 :            : 
     129                 :            : // Fatal errors are treated as server errors but also result in termination
     130                 :            : // of the server.
     131                 :            : static void
     132                 :          0 : fatal (const string &msg)
     133                 :            : {
     134                 :          0 :   server_error (msg);
     135                 :          0 :   cleanup ();
     136                 :          0 :   exit (1);
     137                 :            : }
     138                 :            : 
     139                 :            : // Argument handling
     140                 :            : static void
     141                 :          0 : process_a (const string &arg)
     142                 :            : {
     143                 :          0 :   arch = arg;
     144         [ #  # ]:          0 :   stap_options += " -a " + arg;
     145                 :          0 : }
     146                 :            : 
     147                 :            : static void
     148                 :          0 : process_r (const string &arg)
     149                 :            : {
     150         [ #  # ]:          0 :   if (arg[0] == '/') // fully specified path
     151         [ #  # ]:          0 :     uname_r = kernel_release_from_build_tree (arg);
     152                 :            :   else
     153                 :          0 :     uname_r = arg;
     154         [ #  # ]:          0 :   stap_options += " -r " + arg; // Pass the argument to stap directly.
     155                 :          0 : }
     156                 :            : 
     157                 :            : static void
     158                 :          5 : process_log (const char *arg)
     159                 :            : {
     160                 :          5 :   start_log (arg);
     161                 :          5 : }
     162                 :            : 
     163                 :            : static void
     164                 :          5 : parse_options (int argc, char **argv)
     165                 :            : {
     166                 :            :   // Examine the command line. This is the command line for us (stap-serverd) not the command
     167                 :            :   // line for spawned stap instances.
     168                 :          5 :   optind = 1;
     169                 :          6 :   while (true)
     170                 :            :     {
     171                 :            :       char *num_endptr;
     172                 :            :       long port_tmp;
     173                 :            :       // NB: The values of these enumerators must not conflict with the values of ordinary
     174                 :            :       // characters, since those are returned by getopt_long for short options.
     175                 :            :       enum {
     176                 :            :         LONG_OPT_PORT = 256,
     177                 :            :         LONG_OPT_SSL,
     178                 :            :         LONG_OPT_LOG,
     179                 :            :         LONG_OPT_MAXTHREADS
     180                 :            :       };
     181                 :            :       static struct option long_options[] = {
     182                 :            :         { "port", 1, NULL, LONG_OPT_PORT },
     183                 :            :         { "ssl", 1, NULL, LONG_OPT_SSL },
     184                 :            :         { "log", 1, NULL, LONG_OPT_LOG },
     185                 :            :         { "max-threads", 1, NULL, LONG_OPT_MAXTHREADS },
     186                 :            :         { NULL, 0, NULL, 0 }
     187                 :            :       };
     188                 :         11 :       int grc = getopt_long (argc, argv, "a:B:D:I:kPr:R:", long_options, NULL);
     189         [ +  + ]:         11 :       if (grc < 0)
     190                 :            :         break;
     191   [ -  -  -  -  :          6 :       switch (grc)
          -  -  -  -  -  
             -  +  +  -  
                      - ]
     192                 :            :         {
     193                 :            :         case 'a':
     194 [ #  # ][ #  # ]:          0 :           process_a (optarg);
                 [ #  # ]
     195                 :          0 :           break;
     196                 :            :         case 'B':
     197 [ #  # ][ #  # ]:          0 :           B_options += string (" -") + (char)grc + optarg;
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     198 [ #  # ][ #  # ]:          0 :           stap_options += string (" -") + (char)grc + optarg;
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     199                 :          0 :           break;
     200                 :            :         case 'D':
     201 [ #  # ][ #  # ]:          0 :           D_options += string (" -") + (char)grc + optarg;
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     202 [ #  # ][ #  # ]:          0 :           stap_options += string (" -") + (char)grc + optarg;
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     203                 :          0 :           break;
     204                 :            :         case 'I':
     205 [ #  # ][ #  # ]:          0 :           I_options += string (" -") + (char)grc + optarg;
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     206 [ #  # ][ #  # ]:          0 :           stap_options += string (" -") + (char)grc + optarg;
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     207                 :          0 :           break;
     208                 :            :         case 'k':
     209                 :          0 :           keep_temp = true;
     210                 :          0 :           break;
     211                 :            :         case 'P':
     212                 :          0 :           use_db_password = true;
     213                 :          0 :           break;
     214                 :            :         case 'r':
     215 [ #  # ][ #  # ]:          0 :           process_r (optarg);
                 [ #  # ]
     216                 :          0 :           break;
     217                 :            :         case 'R':
     218 [ #  # ][ #  # ]:          0 :           R_option = string (" -") + (char)grc + optarg;
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     219 [ #  # ][ #  # ]:          0 :           stap_options += string (" -") + (char)grc + optarg;
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     220                 :          0 :           break;
     221                 :            :         case LONG_OPT_PORT:
     222                 :          0 :           port_tmp =  strtol (optarg, &num_endptr, 10);
     223         [ #  # ]:          0 :           if (*num_endptr != '\0')
     224 [ #  # ][ #  # ]:          0 :             fatal (_F("%s: cannot parse number '--port=%s'", argv[0], optarg));
                 [ #  # ]
     225 [ #  # ][ #  # ]:          0 :           else if (port_tmp < 0 || port_tmp > 65535)
     226                 :          0 :             fatal (_F("%s: invalid entry: port must be between 0 and 65535 '--port=%s'", argv[0],
     227 [ #  # ][ #  # ]:          0 :                       optarg));
                 [ #  # ]
     228                 :            :           else
     229                 :          0 :             port = (unsigned short) port_tmp;
     230                 :          0 :           break;
     231                 :            :         case LONG_OPT_SSL:
     232         [ #  # ]:          0 :           cert_db_path = optarg;
     233                 :          0 :           break;
     234                 :            :         case LONG_OPT_LOG:
     235         [ +  - ]:          5 :           process_log (optarg);
     236                 :          5 :           break;
     237                 :            :         case LONG_OPT_MAXTHREADS:
     238                 :          1 :           max_threads = strtol (optarg, &num_endptr, 0);
     239         [ -  + ]:          1 :           if (*num_endptr != '\0')
     240 [ #  # ][ #  # ]:          0 :             fatal (_F("%s: cannot parse number '--max-threads=%s'", argv[0], optarg));
                 [ #  # ]
     241         [ -  + ]:          1 :           else if (max_threads < 0)
     242                 :          0 :             fatal (_F("%s: invalid entry: max threads must not be negative '--max-threads=%s'",
     243 [ #  # ][ #  # ]:          0 :                       argv[0], optarg));
                 [ #  # ]
     244                 :          1 :           break;
     245                 :            :         case '?':
     246                 :            :           // Invalid/unrecognized option given. Message has already been issued.
     247                 :          0 :           break;
     248                 :            :         default:
     249                 :            :           // Reached when one added a getopt option but not a corresponding switch/case:
     250         [ #  # ]:          0 :           if (optarg)
     251 [ #  # ][ #  # ]:          0 :             server_error (_F("%s: unhandled option '%c %s'", argv[0], (char)grc, optarg));
                 [ #  # ]
     252                 :            :           else
     253 [ #  # ][ #  # ]:          0 :             server_error (_F("%s: unhandled option '%c'", argv[0], (char)grc));
                 [ #  # ]
     254                 :          0 :           break;
     255                 :            :         }
     256                 :            :     }
     257                 :            : 
     258         [ -  + ]:          5 :   for (int i = optind; i < argc; i++)
     259         [ #  # ]:          0 :     server_error (_F("%s: unrecognized argument '%s'", argv[0], argv[i]));
     260                 :          5 : }
     261                 :            : 
     262                 :            : static string
     263                 :          5 : server_cert_file ()
     264                 :            : {
     265         [ +  - ]:          5 :   return server_cert_db_path () + "/stap.cert";
     266                 :            : }
     267                 :            : 
     268                 :            : // Signal handling. When an interrupt is received, kill any spawned processes
     269                 :            : // and exit.
     270                 :            : extern "C"
     271                 :            : void
     272                 :          5 : handle_interrupt (int sig)
     273                 :            : {
     274                 :          5 :   pending_interrupts++;
     275         [ -  + ]:          5 :   if(pending_interrupts >= 2)
     276                 :            :     {
     277         [ #  # ]:          0 :       log (_F("Received another signal %d, exiting (forced)", sig));
     278                 :          0 :       _exit(0);
     279                 :            :     }
     280         [ +  - ]:          5 :   log (_F("Received signal %d, exiting", sig));
     281                 :          5 : }
     282                 :            : 
     283                 :            : static void
     284                 :          5 : setup_signals (sighandler_t handler)
     285                 :            : {
     286                 :            :   struct sigaction sa;
     287                 :            : 
     288                 :          5 :   memset(&sa, 0, sizeof(sa));
     289                 :          5 :   sa.sa_handler = handler;
     290                 :          5 :   sigemptyset (&sa.sa_mask);
     291         [ +  - ]:          5 :   if (handler != SIG_IGN)
     292                 :            :     {
     293                 :          5 :       sigaddset (&sa.sa_mask, SIGHUP);
     294                 :          5 :       sigaddset (&sa.sa_mask, SIGPIPE);
     295                 :          5 :       sigaddset (&sa.sa_mask, SIGINT);
     296                 :          5 :       sigaddset (&sa.sa_mask, SIGTERM);
     297                 :          5 :       sigaddset (&sa.sa_mask, SIGTTIN);
     298                 :          5 :       sigaddset (&sa.sa_mask, SIGTTOU);
     299                 :          5 :       sigaddset (&sa.sa_mask, SIGXFSZ);
     300                 :          5 :       sigaddset (&sa.sa_mask, SIGXCPU);
     301                 :            :     }
     302                 :          5 :   sa.sa_flags = SA_RESTART;
     303                 :            : 
     304                 :          5 :   sigaction (SIGHUP, &sa, NULL);
     305                 :          5 :   sigaction (SIGPIPE, &sa, NULL);
     306                 :          5 :   sigaction (SIGINT, &sa, NULL);
     307                 :          5 :   sigaction (SIGTERM, &sa, NULL);
     308                 :          5 :   sigaction (SIGTTIN, &sa, NULL);
     309                 :          5 :   sigaction (SIGTTOU, &sa, NULL);
     310                 :          5 :   sigaction (SIGXFSZ, &sa, NULL);
     311                 :          5 :   sigaction (SIGXCPU, &sa, NULL);
     312                 :          5 : }
     313                 :            : 
     314                 :            : #if HAVE_AVAHI
     315                 :            : static AvahiEntryGroup *avahi_group = NULL;
     316                 :            : static AvahiThreadedPoll *avahi_threaded_poll = NULL;
     317                 :            : static char *avahi_service_name = NULL;
     318                 :            : static AvahiClient *avahi_client = 0;
     319                 :            : 
     320                 :            : static void create_services (AvahiClient *c);
     321                 :            : 
     322                 :            : static void
     323                 :         15 : entry_group_callback (
     324                 :            :   AvahiEntryGroup *g,
     325                 :            :   AvahiEntryGroupState state,
     326                 :            :   AVAHI_GCC_UNUSED void *userdata
     327                 :            : ) {
     328 [ +  + ][ -  + ]:         15 :   assert(g == avahi_group || avahi_group == NULL);
     329                 :         15 :   avahi_group = g;
     330                 :            : 
     331                 :            :   // Called whenever the entry group state changes.
     332   [ +  -  -  +  :         15 :   switch (state)
                      - ]
     333                 :            :     {
     334                 :            :     case AVAHI_ENTRY_GROUP_ESTABLISHED:
     335                 :            :       // The entry group has been established successfully.
     336         [ +  - ]:          5 :       log (_F("Service '%s' successfully established.", avahi_service_name));
     337                 :          5 :       break;
     338                 :            : 
     339                 :            :     case AVAHI_ENTRY_GROUP_COLLISION: {
     340                 :            :       char *n;
     341                 :            :       // A service name collision with a remote service.
     342                 :            :       // happened. Let's pick a new name.
     343                 :          0 :       n = avahi_alternative_service_name (avahi_service_name);
     344                 :          0 :       avahi_free (avahi_service_name);
     345                 :          0 :       avahi_service_name = n;
     346         [ #  # ]:          0 :       server_error (_F("Avahi service name collision, renaming service to '%s'", avahi_service_name));
     347                 :            : 
     348                 :            :       // And recreate the services.
     349                 :          0 :       create_services (avahi_entry_group_get_client (g));
     350                 :          0 :       break;
     351                 :            :     }
     352                 :            : 
     353                 :            :     case AVAHI_ENTRY_GROUP_FAILURE:
     354                 :          0 :       server_error (_F("Avahi entry group failure: %s",
     355         [ #  # ]:          0 :                   avahi_strerror (avahi_client_errno (avahi_entry_group_get_client (g)))));
     356                 :            :       // Some kind of failure happened while we were registering our services.
     357                 :          0 :       avahi_threaded_poll_stop (avahi_threaded_poll);
     358                 :          0 :       break;
     359                 :            : 
     360                 :            :     case AVAHI_ENTRY_GROUP_UNCOMMITED:
     361                 :            :     case AVAHI_ENTRY_GROUP_REGISTERING:
     362                 :         10 :       break;
     363                 :            :     }
     364                 :         15 : }
     365                 :            : 
     366                 :            : static void
     367                 :          5 : create_services (AvahiClient *c) {
     368         [ -  + ]:          5 :   assert (c);
     369                 :            : 
     370                 :            :   // If this is the first time we're called, let's create a new
     371                 :            :   // entry group if necessary.
     372         [ +  - ]:          5 :   if (! avahi_group)
     373         [ -  + ]:          5 :     if (! (avahi_group = avahi_entry_group_new (c, entry_group_callback, NULL)))
     374                 :            :       {
     375                 :          0 :         server_error (_F("avahi_entry_group_new () failed: %s",
     376         [ #  # ]:          0 :                     avahi_strerror (avahi_client_errno (c))));
     377                 :          0 :           goto fail;
     378                 :            :       }
     379                 :            : 
     380                 :            :   // If the group is empty (either because it was just created, or
     381                 :            :   // because it was reset previously, add our entries.
     382         [ +  - ]:          5 :   if (avahi_entry_group_is_empty (avahi_group))
     383                 :            :     {
     384 [ +  - ][ +  - ]:          5 :       log (_F("Adding Avahi service '%s'", avahi_service_name));
                 [ +  - ]
     385                 :            : 
     386                 :            :       // Create the txt tags that will be registered with our service.
     387 [ +  - ][ +  - ]:          5 :       string sysinfo = "sysinfo=" + uname_r + " " + arch;
         [ +  - ][ +  - ]
                 [ +  - ]
     388         [ +  - ]:          5 :       string certinfo = "certinfo=" + cert_serial_number;
     389 [ +  - ][ +  - ]:          5 :       string version = string ("version=") + CURRENT_CS_PROTOCOL_VERSION;;
                 [ +  - ]
     390         [ +  - ]:          5 :       string optinfo = "optinfo=";
     391         [ +  - ]:          5 :       string separator;
     392                 :            :       // These option strings already have a leading space.
     393 [ +  - ][ -  + ]:          5 :       if (! R_option.empty ())
     394                 :            :         {
     395 [ #  # ][ #  # ]:          0 :           optinfo += R_option.substr(1);
                 [ #  # ]
     396         [ #  # ]:          0 :           separator = " ";
     397                 :            :         }
     398 [ +  - ][ -  + ]:          5 :       if (! B_options.empty ())
     399                 :            :         {
     400 [ #  # ][ #  # ]:          0 :           optinfo += separator + B_options.substr(1);
         [ #  # ][ #  # ]
                 [ #  # ]
     401         [ #  # ]:          0 :           separator = " ";
     402                 :            :         }
     403 [ +  - ][ -  + ]:          5 :       if (! D_options.empty ())
     404                 :            :         {
     405 [ #  # ][ #  # ]:          0 :           optinfo += separator + D_options.substr(1);
         [ #  # ][ #  # ]
                 [ #  # ]
     406         [ #  # ]:          0 :           separator = " ";
     407                 :            :         }
     408 [ +  - ][ -  + ]:          5 :       if (! I_options.empty ())
     409 [ #  # ][ #  # ]:          0 :         optinfo += separator + I_options.substr(1);
         [ #  # ][ #  # ]
                 [ #  # ]
     410                 :            : 
     411                 :            :       // We will now add our service to the entry group. Only services with the
     412                 :            :       // same name should be put in the same entry group.
     413                 :            :       int ret;
     414 [ +  - ][ -  + ]:          5 :       if ((ret = avahi_entry_group_add_service (avahi_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
     415                 :            :                                                 (AvahiPublishFlags)0,
     416                 :            :                                                 avahi_service_name, "_stap._tcp", NULL, NULL, port,
     417                 :            :                                                 sysinfo.c_str (), optinfo.c_str (),
     418 [ +  - ][ +  - ]:          5 :                                                 version.c_str (), certinfo.c_str (), NULL)) < 0)
         [ +  - ][ +  - ]
     419                 :            :         {
     420         [ #  # ]:          0 :           if (ret == AVAHI_ERR_COLLISION)
     421                 :            :             goto collision;
     422                 :            : 
     423 [ #  # ][ #  # ]:          0 :           server_error (_F("Failed to add _stap._tcp service: %s", avahi_strerror (ret)));
         [ #  # ][ #  # ]
     424                 :            :           goto fail;
     425                 :            :         }
     426                 :            : 
     427                 :            :       // Tell the server to register the service.
     428 [ +  - ][ -  + ]:          5 :       if ((ret = avahi_entry_group_commit (avahi_group)) < 0)
     429                 :            :         {
     430 [ #  # ][ #  # ]:          5 :           server_error (_F("Failed to commit avahi entry group: %s", avahi_strerror (ret)));
         [ #  # ][ #  # ]
     431                 :            :           goto fail;
     432         [ +  - ]:          5 :         }
              [ -  -  + ]
                 [ +  - ]
              [ -  -  + ]
                 [ +  - ]
              [ -  -  + ]
                 [ +  - ]
              [ -  -  + ]
                 [ +  - ]
              [ +  -  - ]
     433                 :            :     }
     434                 :          5 :   return;
     435                 :            : 
     436                 :            :  collision:
     437                 :            :   // A service name collision with a local service happened. Let's
     438                 :            :   // pick a new name.
     439                 :            :   char *n;
     440                 :          0 :   n = avahi_alternative_service_name (avahi_service_name);
     441                 :          0 :   avahi_free(avahi_service_name);
     442                 :          0 :   avahi_service_name = n;
     443         [ #  # ]:          0 :   server_error (_F("Avahi service name collision, renaming service to '%s'", avahi_service_name));
     444                 :          0 :   avahi_entry_group_reset (avahi_group);
     445                 :          0 :   create_services (c);
     446                 :          0 :   return;
     447                 :            : 
     448                 :            :  fail:
     449                 :          0 :   avahi_entry_group_reset (avahi_group);
     450                 :          5 :   avahi_threaded_poll_stop (avahi_threaded_poll);
     451                 :            : }
     452                 :            : 
     453                 :            : static void
     454                 :          5 : client_callback (AvahiClient *c, AvahiClientState state, AVAHI_GCC_UNUSED void * userdata)
     455                 :            : {
     456         [ -  + ]:          5 :   assert(c);
     457                 :            : 
     458                 :            :   // Called whenever the client or server state changes.
     459   [ +  -  -  -  :          5 :   switch (state)
                      - ]
     460                 :            :     {
     461                 :            :     case AVAHI_CLIENT_S_RUNNING:
     462                 :            :       // The server has startup successfully and registered its host
     463                 :            :       // name on the network, so it's time to create our services.
     464                 :          5 :       create_services (c);
     465                 :          5 :       break;
     466                 :            : 
     467                 :            :     case AVAHI_CLIENT_FAILURE:
     468         [ #  # ]:          0 :       server_error (_F("Avahi client failure: %s", avahi_strerror (avahi_client_errno (c))));
     469                 :          0 :       avahi_threaded_poll_stop (avahi_threaded_poll);
     470                 :          0 :       break;
     471                 :            : 
     472                 :            :     case AVAHI_CLIENT_S_COLLISION:
     473                 :            :       // Let's drop our registered services. When the server is back
     474                 :            :       // in AVAHI_SERVER_RUNNING state we will register them
     475                 :            :       // again with the new host name.
     476                 :            :       // Fall through ...
     477                 :            :     case AVAHI_CLIENT_S_REGISTERING:
     478                 :            :       // The server records are now being established. This
     479                 :            :       // might be caused by a host name change. We need to wait
     480                 :            :       // for our own records to register until the host name is
     481                 :            :       // properly esatblished.
     482         [ #  # ]:          0 :       if (avahi_group)
     483                 :          0 :         avahi_entry_group_reset (avahi_group);
     484                 :          0 :       break;
     485                 :            : 
     486                 :            :     case AVAHI_CLIENT_CONNECTING:
     487                 :          0 :       break;
     488                 :            :     }
     489                 :          5 : }
     490                 :            :  
     491                 :            : static void
     492                 :         10 : avahi_cleanup () {
     493         [ +  + ]:         10 :   if (avahi_service_name)
     494         [ +  - ]:          5 :     log (_F("Removing Avahi service '%s'", avahi_service_name));
     495                 :            : 
     496                 :            :   // Stop the avahi client, if it's running
     497         [ +  + ]:         10 :   if (avahi_threaded_poll)
     498                 :          5 :     avahi_threaded_poll_stop (avahi_threaded_poll);
     499                 :            : 
     500                 :            :   // Clean up the avahi objects. The order of freeing these is significant.
     501         [ +  + ]:         10 :   if (avahi_group) {
     502                 :          5 :     avahi_entry_group_reset (avahi_group);
     503                 :          5 :     avahi_entry_group_free (avahi_group);
     504                 :          5 :     avahi_group = 0;
     505                 :            :   }
     506         [ +  + ]:         10 :   if (avahi_client) {
     507                 :          5 :     avahi_client_free (avahi_client);
     508                 :          5 :     avahi_client = 0;
     509                 :            :   }
     510         [ +  + ]:         10 :   if (avahi_threaded_poll) {
     511                 :          5 :     avahi_threaded_poll_free (avahi_threaded_poll);
     512                 :          5 :     avahi_threaded_poll = 0;
     513                 :            :   }
     514         [ +  + ]:         10 :   if (avahi_service_name) {
     515                 :          5 :     avahi_free (avahi_service_name);
     516                 :          5 :     avahi_service_name = 0;
     517                 :            :   }
     518                 :         10 : }
     519                 :            : 
     520                 :            : // The entry point for the avahi client thread.
     521                 :            : static void
     522                 :          5 : avahi_publish_service (CERTCertificate *cert)
     523                 :            : {
     524 [ +  - ][ +  - ]:          5 :   cert_serial_number = get_cert_serial_number (cert);
                 [ +  - ]
     525         [ +  - ]:          5 :   string buf;
     526                 :            :   try
     527                 :            :     {
     528 [ +  - ][ +  - ]:          5 :       buf = "Systemtap Compile Server, pid=" + lex_cast (getpid ());
         [ +  - ][ +  - ]
                 [ +  - ]
     529                 :            :     }
     530         [ #  # ]:            :   catch (const runtime_error &e)
     531                 :            :     {
     532   [ #  #  #  #  :            :       server_error(_F("Failed to cast pid '%d' to a string: %s", getpid(), e.what()));
                   #  # ]
     533                 :            :       return;
     534                 :            :     }
     535 [ +  - ][ +  - ]:          5 :   avahi_service_name = avahi_strdup (buf.c_str ());
     536                 :            : 
     537                 :            :   // Allocate main loop object.
     538 [ +  - ][ -  + ]:          5 :   if (! (avahi_threaded_poll = avahi_threaded_poll_new ()))
     539                 :            :     {
     540 [ #  # ][ #  # ]:          0 :       server_error (_("Failed to create avahi threaded poll object."));
                 [ #  # ]
     541                 :            :       return;
     542                 :            :     }
     543                 :            : 
     544                 :            :   // Always allocate a new client.
     545                 :            :   int error;
     546                 :            :   avahi_client = avahi_client_new (avahi_threaded_poll_get (avahi_threaded_poll),
     547                 :            :                                    (AvahiClientFlags)0,
     548 [ +  - ][ +  - ]:          5 :                                    client_callback, NULL, & error);
     549                 :            :   // Check whether creating the client object succeeded.
     550         [ -  + ]:          5 :   if (! avahi_client)
     551                 :            :     {
     552 [ #  # ][ #  # ]:          0 :       server_error (_F("Failed to create avahi client: %s", avahi_strerror(error)));
         [ #  # ][ #  # ]
     553                 :            :       return;
     554                 :            :     }
     555                 :            : 
     556                 :            :   // Run the main loop.
     557         [ +  - ]:          5 :   avahi_threaded_poll_start (avahi_threaded_poll);
     558                 :            : 
     559         [ +  - ]:          5 :   return;
     560                 :            : }
     561                 :            : #endif // HAVE_AVAHI
     562                 :            : 
     563                 :            : static void
     564                 :          5 : advertise_presence (CERTCertificate *cert __attribute ((unused)))
     565                 :            : {
     566                 :            : #if HAVE_AVAHI
     567                 :          5 :   avahi_publish_service (cert);
     568                 :            : #else
     569                 :            :   server_error (_("Unable to advertise presence on the network. Avahi is not available"));
     570                 :            : #endif
     571                 :          5 : }
     572                 :            : 
     573                 :            : static void
     574                 :         10 : unadvertise_presence ()
     575                 :            : {
     576                 :            : #if HAVE_AVAHI
     577                 :         10 :   avahi_cleanup ();
     578                 :            : #endif
     579                 :         10 : }
     580                 :            : 
     581                 :            : static void
     582                 :          5 : initialize (int argc, char **argv) {
     583                 :          5 :   pending_interrupts = 0;
     584                 :          5 :   setup_signals (& handle_interrupt);
     585                 :            : 
     586                 :            :   // Seed the random number generator. Used to generate noise used during key generation.
     587                 :          5 :   srand (time (NULL));
     588                 :            : 
     589                 :            :   // Initial values.
     590                 :          5 :   use_db_password = false;
     591                 :          5 :   port = 0;
     592                 :          5 :   max_threads = sysconf( _SC_NPROCESSORS_ONLN ); // Default to number of processors
     593                 :          5 :   keep_temp = false;
     594                 :            :   struct utsname utsname;
     595                 :          5 :   uname (& utsname);
     596         [ +  - ]:          5 :   uname_r = utsname.release;
     597 [ +  - ][ +  - ]:          5 :   arch = normalize_machine (utsname.machine);
         [ +  - ][ +  - ]
                 [ +  - ]
     598                 :            : 
     599                 :            :   // Parse the arguments. This also starts the server log, if any, and should be done before
     600                 :            :   // any messages are issued.
     601         [ +  - ]:          5 :   parse_options (argc, argv);
     602                 :            : 
     603                 :            :   // PR11197: security prophylactics.
     604                 :            :   // Reject use as root, except via a special environment variable.
     605         [ -  + ]:          5 :   if (! getenv ("STAP_PR11197_OVERRIDE")) {
     606         [ #  # ]:          0 :     if (geteuid () == 0)
     607 [ #  # ][ #  # ]:          0 :       fatal ("For security reasons, invocation of stap-serverd as root is not supported.");
                 [ #  # ]
     608                 :            :   }
     609                 :            : 
     610         [ +  - ]:          5 :   struct passwd *pw = getpwuid (geteuid ());
     611         [ -  + ]:          5 :   if (! pw)
     612 [ #  # ][ #  # ]:          0 :     fatal (_F("Unable to determine effective user name: %s", strerror (errno)));
                 [ #  # ]
     613         [ +  - ]:          5 :   string username = pw->pw_name;
     614                 :          5 :   pid_t pid = getpid ();
     615 [ +  - ][ +  - ]:          5 :   log (_F("===== compile server pid %d starting as %s =====", pid, username.c_str ()));
         [ +  - ][ +  - ]
     616                 :            : 
     617                 :            :   // Where is the ssl certificate/key database?
     618 [ +  - ][ +  - ]:          5 :   if (cert_db_path.empty ())
     619 [ +  - ][ +  - ]:          5 :     cert_db_path = server_cert_db_path ();
                 [ +  - ]
     620                 :            : 
     621                 :            :   // Make sure NSPR is initialized. Must be done before NSS is initialized
     622         [ +  - ]:          5 :   PR_Init (PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1);
     623                 :            :   /* Set the cert database password callback. */
     624 [ +  - ][ +  - ]:          5 :   PK11_SetPasswordFunc (nssPasswordCallback);
     625                 :          5 : }
     626                 :            : 
     627                 :            : static void
     628                 :          5 : cleanup ()
     629                 :            : {
     630                 :          5 :   unadvertise_presence ();
     631                 :          5 :   end_log ();
     632                 :          5 : }
     633                 :            : 
     634                 :            : /* Function:  readDataFromSocket()
     635                 :            :  *
     636                 :            :  * Purpose:  Read data from the socket into a temporary file.
     637                 :            :  *
     638                 :            :  */
     639                 :            : static PRInt32
     640                 :         36 : readDataFromSocket(PRFileDesc *sslSocket, const char *requestFileName)
     641                 :            : {
     642                 :         36 :   PRFileDesc *local_file_fd = 0;
     643                 :            :   PRInt32     numBytesExpected;
     644                 :            :   PRInt32     numBytesRead;
     645                 :            :   PRInt32     numBytesWritten;
     646                 :         36 :   PRInt32     totalBytes = 0;
     647                 :            : #define READ_BUFFER_SIZE 4096
     648                 :            :   char        buffer[READ_BUFFER_SIZE];
     649                 :            : 
     650                 :            :   /* Read the number of bytes to be received.  */
     651                 :            :   /* XXX: impose a limit to prevent disk space consumption DoS */
     652         [ +  - ]:         36 :   numBytesRead = PR_Read (sslSocket, & numBytesExpected, sizeof (numBytesExpected));
     653         [ -  + ]:         36 :   if (numBytesRead == 0) /* EOF */
     654                 :            :     {
     655 [ #  # ][ #  # ]:          0 :       server_error (_("Error reading size of request file"));
                 [ #  # ]
     656                 :          0 :       goto done;
     657                 :            :     }
     658         [ -  + ]:         36 :   if (numBytesRead < 0)
     659                 :            :     {
     660 [ #  # ][ #  # ]:          0 :       server_error (_("Error in PR_Read"));
                 [ #  # ]
     661         [ #  # ]:          0 :       nssError ();
     662                 :          0 :       goto done;
     663                 :            :     }
     664                 :            : 
     665                 :            :   /* Convert numBytesExpected from network byte order to host byte order.  */
     666                 :         36 :   numBytesExpected = ntohl (numBytesExpected);
     667                 :            : 
     668                 :            :   /* If 0 bytes are expected, then we were contacted only to obtain our certificate.
     669                 :            :      There is no client request. */
     670         [ +  + ]:         36 :   if (numBytesExpected == 0)
     671                 :          1 :     return 0;
     672                 :            : 
     673                 :            :   /* Open the output file.  */
     674                 :            :   local_file_fd = PR_Open(requestFileName, PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE,
     675         [ +  - ]:         35 :                           PR_IRUSR | PR_IWUSR);
     676         [ -  + ]:         35 :   if (local_file_fd == NULL)
     677                 :            :     {
     678 [ #  # ][ #  # ]:          0 :       server_error (_F("Could not open output file %s", requestFileName));
                 [ #  # ]
     679         [ #  # ]:          0 :       nssError ();
     680                 :          0 :       return -1;
     681                 :            :     }
     682                 :            : 
     683                 :            :   /* Read until EOF or until the expected number of bytes has been read. */
     684         [ +  + ]:         70 :   for (totalBytes = 0; totalBytes < numBytesExpected; totalBytes += numBytesRead)
     685                 :            :     {
     686         [ +  - ]:         35 :       numBytesRead = PR_Read(sslSocket, buffer, READ_BUFFER_SIZE);
     687         [ -  + ]:         35 :       if (numBytesRead == 0)
     688                 :          0 :         break;  /* EOF */
     689         [ -  + ]:         35 :       if (numBytesRead < 0)
     690                 :            :         {
     691 [ #  # ][ #  # ]:          0 :           server_error (_("Error in PR_Read"));
                 [ #  # ]
     692         [ #  # ]:          0 :           nssError ();
     693                 :          0 :           goto done;
     694                 :            :         }
     695                 :            : 
     696                 :            :       /* Write to the request file. */
     697         [ +  - ]:         35 :       numBytesWritten = PR_Write(local_file_fd, buffer, numBytesRead);
     698    [ + ][ -  + ]:         34 :       if (numBytesWritten < 0 || (numBytesWritten != numBytesRead))
     699                 :            :         {
     700 [ -  - ][ #  # ]:          0 :           server_error (_F("Could not write to output file %s", requestFileName));
                 [ #  # ]
     701         [ #  # ]:          0 :           nssError ();
     702                 :          0 :           goto done;
     703                 :            :         }
     704                 :            :     }
     705                 :            : 
     706         [ -  + ]:         35 :   if (totalBytes != numBytesExpected)
     707                 :            :     {
     708                 :          0 :       server_error (_F("Expected %d bytes, got %d while reading client request from socket",
     709 [ #  # ][ #  # ]:          0 :                           numBytesExpected, totalBytes));
                 [ #  # ]
     710                 :          0 :       goto done;
     711                 :            :     }
     712                 :            : 
     713                 :            :  done:
     714         [ +  - ]:         35 :   if (local_file_fd)
     715         [ +  - ]:         35 :     PR_Close (local_file_fd);
     716                 :         36 :   return totalBytes;
     717                 :            : }
     718                 :            : 
     719                 :            : /* Function:  setupSSLSocket()
     720                 :            :  *
     721                 :            :  * Purpose:  Configure a socket for SSL.
     722                 :            :  *
     723                 :            :  *
     724                 :            :  */
     725                 :            : static PRFileDesc * 
     726                 :         36 : setupSSLSocket (PRFileDesc *tcpSocket, CERTCertificate *cert, SECKEYPrivateKey *privKey)
     727                 :            : {
     728                 :            :   PRFileDesc *sslSocket;
     729                 :            :   SSLKEAType  certKEA;
     730                 :            :   SECStatus   secStatus;
     731                 :            : 
     732                 :            :   /* Inport the socket into SSL.  */
     733                 :         36 :   sslSocket = SSL_ImportFD (NULL, tcpSocket);
     734         [ -  + ]:         36 :   if (sslSocket == NULL)
     735                 :            :     {
     736 [ #  # ][ #  # ]:          0 :       server_error (_("Could not import socket into SSL"));
                 [ #  # ]
     737                 :          0 :       nssError ();
     738                 :          0 :       return NULL;
     739                 :            :     }
     740                 :            :    
     741                 :            :   /* Set the appropriate flags. */
     742                 :         36 :   secStatus = SSL_OptionSet (sslSocket, SSL_SECURITY, PR_TRUE);
     743         [ -  + ]:         36 :   if (secStatus != SECSuccess)
     744                 :            :     {
     745 [ #  # ][ #  # ]:          0 :       server_error (_("Error setting SSL security for socket"));
                 [ #  # ]
     746                 :          0 :       nssError ();
     747                 :          0 :       return NULL;
     748                 :            :     }
     749                 :            : 
     750                 :         36 :   secStatus = SSL_OptionSet(sslSocket, SSL_HANDSHAKE_AS_SERVER, PR_TRUE);
     751         [ -  + ]:         36 :   if (secStatus != SECSuccess)
     752                 :            :     {
     753 [ #  # ][ #  # ]:          0 :       server_error (_("Error setting handshake as server for socket"));
                 [ #  # ]
     754                 :          0 :       nssError ();
     755                 :          0 :       return NULL;
     756                 :            :     }
     757                 :            : 
     758                 :         36 :   secStatus = SSL_OptionSet(sslSocket, SSL_REQUEST_CERTIFICATE, PR_FALSE);
     759         [ -  + ]:         36 :   if (secStatus != SECSuccess)
     760                 :            :     {
     761 [ #  # ][ #  # ]:          0 :       server_error (_("Error setting SSL client authentication mode for socket"));
                 [ #  # ]
     762                 :          0 :       nssError ();
     763                 :          0 :       return NULL;
     764                 :            :     }
     765                 :            : 
     766                 :         36 :   secStatus = SSL_OptionSet(sslSocket, SSL_REQUIRE_CERTIFICATE, PR_FALSE);
     767         [ -  + ]:         36 :   if (secStatus != SECSuccess)
     768                 :            :     {
     769 [ #  # ][ #  # ]:          0 :       server_error (_("Error setting SSL client authentication mode for socket"));
                 [ #  # ]
     770                 :          0 :       nssError ();
     771                 :          0 :       return NULL;
     772                 :            :     }
     773                 :            : 
     774                 :            :   /* Set the appropriate callback routines. */
     775                 :            : #if 0 /* use the default */
     776                 :            :   secStatus = SSL_AuthCertificateHook (sslSocket, myAuthCertificate, CERT_GetDefaultCertDB());
     777                 :            :   if (secStatus != SECSuccess)
     778                 :            :     {
     779                 :            :       nssError ();
     780                 :            :       server_error (_("Error in SSL_AuthCertificateHook"));
     781                 :            :       return NULL;
     782                 :            :     }
     783                 :            : #endif
     784                 :            : #if 0 /* Use the default */
     785                 :            :   secStatus = SSL_BadCertHook(sslSocket, (SSLBadCertHandler)myBadCertHandler, &certErr);
     786                 :            :   if (secStatus != SECSuccess)
     787                 :            :     {
     788                 :            :       nssError ();
     789                 :            :       server_error (_("Error in SSL_BadCertHook"));
     790                 :            :       return NULL;
     791                 :            :     }
     792                 :            : #endif
     793                 :            : #if 0 /* no handshake callback */
     794                 :            :   secStatus = SSL_HandshakeCallback(sslSocket, myHandshakeCallback, NULL);
     795                 :            :   if (secStatus != SECSuccess)
     796                 :            :     {
     797                 :            :       server_error (_("Error in SSL_HandshakeCallback"));
     798                 :            :       nssError ();
     799                 :            :       return NULL;
     800                 :            :     }
     801                 :            : #endif
     802                 :            : 
     803                 :         36 :   certKEA = NSS_FindCertKEAType (cert);
     804                 :            : 
     805                 :         36 :   secStatus = SSL_ConfigSecureServer (sslSocket, cert, privKey, certKEA);
     806         [ -  + ]:         36 :   if (secStatus != SECSuccess)
     807                 :            :     {
     808 [ #  # ][ #  # ]:          0 :       server_error (_("Error configuring SSL server"));
                 [ #  # ]
     809                 :          0 :       nssError ();
     810                 :          0 :       return NULL;
     811                 :            :     }
     812                 :            : 
     813                 :         36 :   return sslSocket;
     814                 :            : }
     815                 :            : 
     816                 :            : #if 0 /* No client authentication (for now) and not authenticating after each transaction.  */
     817                 :            : /* Function:  authenticateSocket()
     818                 :            :  *
     819                 :            :  * Purpose:  Perform client authentication on the socket.
     820                 :            :  *
     821                 :            :  */
     822                 :            : static SECStatus
     823                 :            : authenticateSocket (PRFileDesc *sslSocket, PRBool requireCert)
     824                 :            : {
     825                 :            :   CERTCertificate *cert;
     826                 :            :   SECStatus secStatus;
     827                 :            : 
     828                 :            :   /* Returns NULL if client authentication is not enabled or if the
     829                 :            :    * client had no certificate. */
     830                 :            :   cert = SSL_PeerCertificate(sslSocket);
     831                 :            :   if (cert)
     832                 :            :     {
     833                 :            :       /* Client had a certificate, so authentication is through. */
     834                 :            :       CERT_DestroyCertificate(cert);
     835                 :            :       return SECSuccess;
     836                 :            :     }
     837                 :            : 
     838                 :            :   /* Request client to authenticate itself. */
     839                 :            :   secStatus = SSL_OptionSet(sslSocket, SSL_REQUEST_CERTIFICATE, PR_TRUE);
     840                 :            :   if (secStatus != SECSuccess)
     841                 :            :     {
     842                 :            :       server_error (_("Error in SSL_OptionSet:SSL_REQUEST_CERTIFICATE"));
     843                 :            :       nssError ();
     844                 :            :       return SECFailure;
     845                 :            :     }
     846                 :            : 
     847                 :            :   /* If desired, require client to authenticate itself.  Note
     848                 :            :    * SSL_REQUEST_CERTIFICATE must also be on, as above.  */
     849                 :            :   secStatus = SSL_OptionSet(sslSocket, SSL_REQUIRE_CERTIFICATE, requireCert);
     850                 :            :   if (secStatus != SECSuccess)
     851                 :            :     {
     852                 :            :       server_error (_("Error in SSL_OptionSet:SSL_REQUIRE_CERTIFICATE"));
     853                 :            :       nssError ();
     854                 :            :       return SECFailure;
     855                 :            :     }
     856                 :            : 
     857                 :            :   /* Having changed socket configuration parameters, redo handshake. */
     858                 :            :   secStatus = SSL_ReHandshake(sslSocket, PR_TRUE);
     859                 :            :   if (secStatus != SECSuccess)
     860                 :            :     {
     861                 :            :       server_error (_("Error in SSL_ReHandshake"));
     862                 :            :       nssError ();
     863                 :            :       return SECFailure;
     864                 :            :     }
     865                 :            : 
     866                 :            :   /* Force the handshake to complete before moving on. */
     867                 :            :   secStatus = SSL_ForceHandshake(sslSocket);
     868                 :            :   if (secStatus != SECSuccess)
     869                 :            :     {
     870                 :            :       server_error (_("Error in SSL_ForceHandshake"));
     871                 :            :       nssError ();
     872                 :            :       return SECFailure;
     873                 :            :     }
     874                 :            : 
     875                 :            :   return SECSuccess;
     876                 :            : }
     877                 :            : #endif /* No client authentication and not authenticating after each transaction.  */
     878                 :            : 
     879                 :            : /* Function:  writeDataToSocket
     880                 :            :  *
     881                 :            :  * Purpose:  Write the server's response back to the socket.
     882                 :            :  *
     883                 :            :  */
     884                 :            : static SECStatus
     885                 :         35 : writeDataToSocket(PRFileDesc *sslSocket, const char *responseFileName)
     886                 :            : {
     887                 :         35 :   PRFileDesc *local_file_fd = PR_Open (responseFileName, PR_RDONLY, 0);
     888         [ -  + ]:         35 :   if (local_file_fd == NULL)
     889                 :            :     {
     890         [ #  # ]:          0 :       server_error (_F("Could not open input file %s", responseFileName));
     891                 :          0 :       nssError ();
     892                 :          0 :       return SECFailure;
     893                 :            :     }
     894                 :            : 
     895                 :            :   /* Transmit the local file across the socket.
     896                 :            :    */
     897                 :            :   int numBytes = PR_TransmitFile (sslSocket, local_file_fd, 
     898                 :            :                                   NULL, 0,
     899                 :            :                                   PR_TRANSMITFILE_KEEP_OPEN,
     900                 :         35 :                                   PR_INTERVAL_NO_TIMEOUT);
     901                 :            : 
     902                 :            :   /* Error in transmission. */
     903                 :         35 :   SECStatus secStatus = SECSuccess;
     904         [ -  + ]:         35 :   if (numBytes < 0)
     905                 :            :     {
     906 [ #  # ][ #  # ]:          0 :       server_error (_("Error writing response to socket"));
                 [ #  # ]
     907                 :          0 :       nssError ();
     908                 :          0 :       secStatus = SECFailure;
     909                 :            :     }
     910                 :            : 
     911                 :         35 :   PR_Close (local_file_fd);
     912                 :         35 :   return secStatus;
     913                 :            : }
     914                 :            : 
     915                 :            : static void
     916                 :         35 : get_stap_locale (const string &staplang, vector<string> &envVec, string stapstderr, cs_protocol_version *client_version)
     917                 :            : {
     918                 :            :   // If the client version is < 1.6, then no file containing environment
     919                 :            :   // variables defining the locale has been passed.
     920 [ +  - ][ +  - ]:         35 :   if (*client_version < "1.6")
         [ +  - ][ +  - ]
     921                 :            :     return;
     922                 :            : 
     923                 :            :   /* Go through each line of the file, verify it, then add it to the vector */
     924         [ +  - ]:         35 :   ifstream langfile;
     925 [ +  - ][ +  - ]:         35 :   langfile.open(staplang.c_str());
     926 [ +  - ][ -  + ]:         35 :   if (!langfile.is_open())
     927                 :            :     {
     928                 :            :       // Not fatal. Proceed with the environment we have.
     929                 :          0 :       server_error(_F("Unable to open file %s for reading: %s", staplang.c_str(),
     930   [ #  #  #  # ]:          0 :                          strerror (errno)));
         [ #  # ][ #  # ]
     931                 :            :       return;
     932                 :            :     }
     933                 :            : 
     934                 :            :   /* Unpackage internationalization variables and verify their contents */
     935         [ +  - ]:         35 :   map<string, string> envMap; /* To temporarily store the entire array of strings */
     936         [ +  - ]:         35 :   string line;
     937         [ +  - ]:         35 :   const set<string> &locVars = localization_variables();
     938                 :            : 
     939                 :            :   /* Copy the global environ variable into the map */
     940         [ +  - ]:         35 :    if(environ != NULL)
     941                 :            :      {
     942         [ +  + ]:       1610 :       for (unsigned i=0; environ[i]; i++)
     943                 :            :         {
     944         [ +  - ]:       1575 :           string line = (string)environ[i];
     945                 :            : 
     946                 :            :           /* Find the first '=' sign */
     947         [ +  - ]:       1575 :           size_t pos = line.find("=");
     948                 :            : 
     949                 :            :           /* Make sure it found an '=' sign */
     950         [ +  - ]:       1575 :           if(pos != string::npos)
     951                 :            :             /* Everything before the '=' sign is the key, and everything after is the value. */ 
     952 [ +  - ][ +  - ]:       1575 :             envMap[line.substr(0, pos)] = line.substr(pos+1); 
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
     953         [ +  - ]:       1575 :         }
     954                 :            :      }
     955                 :            : 
     956                 :            :   /* Create regular expression objects to verify lines read from file. Should not allow
     957                 :            :      spaces, ctrl characters, etc */
     958                 :            :   regex_t checkre;
     959 [ +  - ][ -  + ]:         35 :   if ((regcomp(&checkre, "^[a-zA-Z0-9@_.=-]*$", REG_EXTENDED | REG_NOSUB) != 0))
     960                 :            :     {
     961                 :            :       // Not fatal. Proceed with the environment we have.
     962 [ #  # ][ #  # ]:          0 :       server_error(_F("Error in regcomp: %s", strerror (errno)));
                 [ #  # ]
     963                 :            :       return;
     964                 :            :     }
     965                 :            : 
     966                 :         44 :   while (1)
     967                 :            :     {
     968         [ +  - ]:         79 :       getline(langfile, line);
     969 [ +  - ][ +  + ]:         79 :       if (!langfile.good())
     970                 :            :         break;
     971                 :            : 
     972                 :            :       /* Extract key and value from the line. Note: value may contain "=". */
     973         [ +  - ]:         44 :       string key;
     974         [ +  - ]:         44 :       string value;
     975                 :            :       size_t pos;
     976         [ +  - ]:         44 :       pos = line.find("=");
     977         [ +  + ]:         44 :       if (pos == string::npos)
     978                 :            :         {
     979 [ +  - ][ +  - ]:          1 :           client_error(_F("Localization key=value line '%s' cannot be parsed", line.c_str()), stapstderr);
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
     980                 :          1 :           continue;
     981                 :            :         }
     982 [ +  - ][ +  - ]:         43 :       key = line.substr(0, pos);
                 [ +  - ]
     983                 :         43 :       pos++;
     984 [ +  - ][ +  - ]:         43 :       value = line.substr(pos);
                 [ +  - ]
     985                 :            : 
     986                 :            :       /* Make sure the key is found in the localization variables global set */
     987 [ +  - ][ +  - ]:         43 :       if (locVars.find(key) == locVars.end())
                 [ -  + ]
     988                 :            :         {
     989                 :            :           // Not fatal. Just ignore it.
     990 [ #  # ][ #  # ]:          0 :           client_error(_F("Localization key '%s' not found in global list", key.c_str()), stapstderr);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
     991                 :          0 :           continue;
     992                 :            :         }
     993                 :            : 
     994                 :            :       /* Make sure the value does not contain illegal characters */
     995 [ +  - ][ +  - ]:         43 :       if ((regexec(&checkre, value.c_str(), (size_t) 0, NULL, 0) != 0))
                 [ +  + ]
     996                 :            :         {
     997                 :            :           // Not fatal. Just ignore it.
     998 [ +  - ][ +  - ]:          1 :           client_error(_F("Localization value '%s' contains illegal characters", value.c_str()), stapstderr);
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
     999                 :          1 :           continue;
    1000                 :            :         }
    1001                 :            : 
    1002                 :            :       /* All is good, copy line into envMap, replacing if already there */
    1003 [ +  - ][ +  - ]:         42 :       envMap[key] = value;
    1004 [ +  - ][ +  + ]:         44 :     }
         [ +  - ][ +  + ]
    1005                 :            : 
    1006 [ +  - ][ -  + ]:         37 :   if (!langfile.eof())
    1007                 :            :     {
    1008                 :            :       // Not fatal. Proceed with what we have.
    1009 [ #  # ][ #  # ]:          0 :       server_error(_F("Error reading file %s: %s", staplang.c_str(), strerror (errno)));
         [ #  # ][ #  # ]
    1010                 :            :     }
    1011                 :            : 
    1012         [ +  - ]:         35 :   regfree(&checkre);
    1013                 :            : 
    1014                 :            :   /* Copy map into vector */
    1015 [ +  - ][ +  - ]:       1618 :   for (map<string, string>::iterator it = envMap.begin(); it != envMap.end(); it++)
                 [ +  + ]
    1016 [ +  - ][ +  - ]:       1618 :     envVec.push_back(it->first + "=" + it->second);
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
         [ -  + ][ +  - ]
         [ -  + ][ +  - ]
                 [ +  - ]
    1017                 :            : }
    1018                 :            : 
    1019                 :            : // Filter paths prefixed with the server's home directory from the given file.
    1020                 :            : //
    1021                 :            : static void
    1022                 :         70 : filter_response_file (const string &file_name, const string &responseDirName)
    1023                 :            : {
    1024         [ +  - ]:         70 :   vector<string> cmd;
    1025                 :            : 
    1026                 :            :   // Filter the server's home directory name
    1027         [ +  - ]:         70 :   cmd.clear();
    1028 [ +  - ][ +  - ]:         70 :   cmd.push_back ("sed");
                 [ +  - ]
    1029 [ +  - ][ +  - ]:         70 :   cmd.push_back ("-i");
                 [ +  - ]
    1030 [ +  - ][ +  - ]:         70 :   cmd.push_back (string ("s,") + get_home_directory () + ",<server>,g");
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
    1031         [ +  - ]:         70 :   cmd.push_back (file_name);
    1032         [ +  - ]:         70 :   stap_system (0, cmd);
    1033                 :            : 
    1034                 :            :   // Filter the server's response directory name
    1035         [ +  - ]:         70 :   cmd.clear();
    1036 [ +  - ][ +  - ]:         70 :   cmd.push_back ("sed");
                 [ +  - ]
    1037 [ +  - ][ +  - ]:         70 :   cmd.push_back ("-i");
                 [ +  - ]
    1038 [ +  - ][ +  - ]:         70 :   cmd.push_back (string ("s,") + responseDirName + ",<server>,g");
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
                 [ +  - ]
    1039         [ +  - ]:         70 :   cmd.push_back (file_name);
    1040 [ +  - ][ +  - ]:         70 :   stap_system (0, cmd);
    1041                 :         70 : }
    1042                 :            : 
    1043                 :            : static privilege_t
    1044                 :         35 : getRequestedPrivilege (const vector<string> &stapargv)
    1045                 :            : {
    1046                 :            :   // The purpose of this function is to find the --privilege or --unprivileged option specified
    1047                 :            :   // by the user on the client side. We need to parse the command line completely, but we can
    1048                 :            :   // exit when we find the first --privilege or --unprivileged option, since stap does not allow
    1049                 :            :   // multiple privilege levels to specified on the same command line.
    1050                 :            :   //
    1051                 :            :   // Note that we need not do any options consistency checking since our spawned stap instance
    1052                 :            :   // will do that.
    1053                 :            :   //
    1054                 :            :   // Create an argv/argc for use by getopt_long.
    1055                 :         35 :   int argc = stapargv.size();
    1056         [ +  - ]:         35 :   char ** argv = new char *[argc + 1];
    1057         [ +  + ]:        242 :   for (unsigned i = 0; i < stapargv.size(); ++i)
    1058                 :        207 :     argv[i] = (char *)stapargv[i].c_str();
    1059                 :         35 :   argv[argc] = NULL;
    1060                 :            : 
    1061                 :         35 :   privilege_t privilege = pr_highest; // Until specified otherwise.
    1062                 :         35 :   optind = 1;
    1063                 :        151 :   while (true)
    1064                 :            :     {
    1065                 :            :       // We need only allow getopt to parse the options until we find a
    1066                 :            :       // --privilege or --unprivileged option.
    1067                 :        186 :       int grc = getopt_long (argc, argv, STAP_SHORT_OPTIONS, stap_long_options, NULL);
    1068         [ +  + ]:        186 :       if (grc < 0)
    1069                 :         32 :         break;
    1070      [ +  +  + ]:        154 :       switch (grc)
    1071                 :            :         {
    1072                 :            :         default:
    1073                 :            :           // We can ignore all options other than --privilege and --unprivileged.
    1074                 :        151 :           break;
    1075                 :            :         case LONG_OPT_PRIVILEGE:
    1076         [ -  + ]:          2 :           if (strcmp (optarg, "stapdev") == 0)
    1077                 :          0 :             privilege = pr_stapdev;
    1078         [ +  + ]:          2 :           else if (strcmp (optarg, "stapsys") == 0)
    1079                 :          1 :             privilege = pr_stapsys;
    1080         [ +  - ]:          1 :           else if (strcmp (optarg, "stapusr") == 0)
    1081                 :          1 :             privilege = pr_stapusr;
    1082                 :            :           else
    1083                 :            :             {
    1084         [ #  # ]:          0 :               server_error (_F("Invalid argument '%s' for --privilege", optarg));
    1085                 :          0 :               privilege = pr_highest;
    1086                 :            :             }
    1087                 :            :           // We have discovered the client side --privilege option. We can exit now since
    1088                 :            :           // stap only tolerates one privilege setting option.
    1089                 :          2 :           goto done; // break 2 switches and a loop
    1090                 :            :         case LONG_OPT_UNPRIVILEGED:
    1091                 :          1 :           privilege = pr_unprivileged;
    1092                 :            :           // We have discovered the client side --unprivileged option. We can exit now since
    1093                 :            :           // stap only tolerates one privilege setting option.
    1094                 :          1 :           goto done; // break 2 switches and a loop
    1095                 :            :         }
    1096                 :            :     }
    1097                 :            :  done:
    1098         [ +  - ]:         35 :   delete[] argv;
    1099                 :         35 :   return privilege;
    1100                 :            : }
    1101                 :            : 
    1102                 :            : /* Run the translator on the data in the request directory, and produce output
    1103                 :            :    in the given output directory. */
    1104                 :            : static void
    1105                 :         35 : handleRequest (const string &requestDirName, const string &responseDirName, string stapstderr)
    1106                 :            : {
    1107         [ +  - ]:         35 :   vector<string> stapargv;
    1108         [ +  - ]:         35 :   cs_protocol_version client_version = "1.0"; // Assumed until discovered otherwise
    1109                 :            :   int rc;
    1110                 :            :   wordexp_t words;
    1111                 :            :   unsigned u;
    1112                 :            :   unsigned i;
    1113                 :            :   FILE* f;
    1114                 :            : 
    1115                 :            :   // Save the server version. Do this early, so the client knows what version of the server
    1116                 :            :   // it is dealing with, even if the request is not fully completed.
    1117         [ +  - ]:         35 :   string stapversion = responseDirName + "/version";
    1118 [ +  - ][ +  - ]:         35 :   f = fopen (stapversion.c_str (), "w");
    1119         [ +  - ]:         35 :   if (f) 
    1120                 :            :     {
    1121         [ +  - ]:         35 :       fputs (CURRENT_CS_PROTOCOL_VERSION, f);
    1122         [ +  - ]:         35 :       fclose(f);
    1123                 :            :     }
    1124                 :            :   else
    1125 [ #  # ][ #  # ]:          0 :     server_error (_F("Unable to open client version file %s", stapversion.c_str ()));
         [ #  # ][ #  # ]
    1126                 :            : 
    1127                 :            :   // Get the client version. The default version is already set. Use it if we fail here.
    1128         [ +  - ]:         35 :   string filename = requestDirName + "/version";
    1129 [ +  - ][ +  - ]:         35 :   if (file_exists (filename))
    1130         [ +  - ]:         35 :     read_from_file (filename, client_version);
    1131 [ +  - ][ +  - ]:         35 :   log (_F("Client version is %s", client_version.v));
                 [ +  - ]
    1132                 :            : 
    1133                 :            :   // The name of the translator executable.
    1134 [ +  - ][ +  - ]:         35 :   stapargv.push_back ((char *)(getenv ("SYSTEMTAP_STAP") ?: STAP_PREFIX "/bin/stap"));
         [ +  - ][ +  - ]
    1135                 :            : 
    1136                 :            :   /* Transcribe stap_options.  We use plain wordexp(3), since these
    1137                 :            :      options are coming from the local trusted user, so malicious
    1138                 :            :      content is not a concern. */
    1139                 :            :   // TODO: Use tokenize here.
    1140 [ +  - ][ +  - ]:         35 :   rc = wordexp (stap_options.c_str (), & words, WRDE_NOCMD|WRDE_UNDEF);
    1141         [ -  + ]:         35 :   if (rc)
    1142                 :            :     {
    1143 [ #  # ][ #  # ]:          0 :       server_error (_("Cannot parse stap options"));
                 [ #  # ]
    1144                 :            :       return;
    1145                 :            :     }
    1146                 :            : 
    1147         [ -  + ]:         35 :   for (u=0; u<words.we_wordc; u++)
    1148 [ #  # ][ #  # ]:          0 :     stapargv.push_back (words.we_wordv[u]);
                 [ #  # ]
    1149                 :            : 
    1150                 :            :   /* Process the saved command line arguments.  Avoid quoting/unquoting errors by
    1151                 :            :      transcribing literally. */
    1152         [ +  - ]:         35 :   string new_staptmpdir = responseDirName + "/stap000000";
    1153         [ +  - ]:         35 :   rc = mkdir(new_staptmpdir.c_str(), 0700);
    1154         [ -  + ]:         35 :   if (rc)
    1155 [ #  # ][ #  # ]:          0 :     server_error(_F("Could not create temporary directory %s", new_staptmpdir.c_str()));
         [ #  # ][ #  # ]
    1156                 :            : 
    1157 [ +  - ][ +  - ]:         35 :   stapargv.push_back("--tmpdir=" + new_staptmpdir);
                 [ +  - ]
    1158                 :            : 
    1159 [ +  - ][ +  - ]:         35 :   stapargv.push_back ("--client-options");
                 [ +  - ]
    1160                 :        137 :   for (i=1 ; ; i++)
    1161                 :            :     {
    1162                 :            :       char stapargfile[PATH_MAX];
    1163                 :            :       FILE* argfile;
    1164                 :            :       struct stat st;
    1165                 :            :       char *arg;
    1166                 :            : 
    1167         [ +  - ]:        137 :       snprintf (stapargfile, PATH_MAX, "%s/argv%d", requestDirName.c_str (), i);
    1168                 :            : 
    1169                 :        137 :       rc = stat(stapargfile, & st);
    1170         [ +  + ]:        137 :       if (rc) break;
    1171                 :            : 
    1172                 :        102 :       arg = (char *)malloc (st.st_size+1);
    1173         [ -  + ]:        102 :       if (!arg)
    1174                 :            :         {
    1175 [ #  # ][ #  # ]:          0 :           server_error (_("Out of memory"));
                 [ #  # ]
    1176                 :            :           return;
    1177                 :            :         }
    1178                 :            : 
    1179         [ +  - ]:        102 :       argfile = fopen(stapargfile, "r");
    1180         [ -  + ]:        102 :       if (! argfile)
    1181                 :            :         {
    1182                 :          0 :           free(arg);
    1183 [ #  # ][ #  # ]:          0 :           server_error (_F("Error opening %s: %s", stapargfile, strerror (errno)));
                 [ #  # ]
    1184                 :            :           return;
    1185                 :            :         }
    1186                 :            : 
    1187         [ +  - ]:        102 :       rc = fread(arg, 1, st.st_size, argfile);
    1188         [ -  + ]:        102 :       if (rc != st.st_size)
    1189                 :            :         {
    1190                 :          0 :           free(arg);
    1191         [ #  # ]:          0 :           fclose(argfile);
    1192 [ #  # ][ #  # ]:          0 :           server_error (_F("Error reading %s: %s", stapargfile, strerror (errno)));
                 [ #  # ]
    1193                 :            :           return;
    1194                 :            :         }
    1195                 :            : 
    1196                 :        102 :       arg[st.st_size] = '\0';
    1197 [ +  - ][ +  - ]:        102 :       stapargv.push_back (arg);
                 [ +  - ]
    1198                 :        102 :       free (arg);
    1199         [ +  - ]:        102 :       fclose (argfile);
    1200                 :            :     }
    1201                 :            : 
    1202         [ +  - ]:         35 :   string stapstdout = responseDirName + "/stdout";
    1203                 :            : 
    1204                 :            :   // NB: Before, when we did not fully parse the client's command line using getopt_long,
    1205                 :            :   // we used to insert a --privilege=XXX option here in case some other argument was mistaken
    1206                 :            :   // for a --privilege or --unprivileged option by our spawned stap. Since we now parse
    1207                 :            :   // the client's command line using getopt_long and share the getopt_long options
    1208                 :            :   // string and table with stap, this is no longer necessary. stap will parse the
    1209                 :            :   // command line identically to the way we have parsed it and will discover the same
    1210                 :            :   // privilege-setting option.
    1211                 :            : 
    1212                 :            :   // Environment variables (possibly empty) to be passed to spawn_and_wait().
    1213         [ +  - ]:         35 :   string staplang = requestDirName + "/locale";
    1214         [ +  - ]:         35 :   vector<string> envVec;
    1215 [ +  - ][ +  - ]:         35 :   get_stap_locale (staplang, envVec, stapstderr, &client_version);
                 [ +  - ]
    1216                 :            : 
    1217                 :            :   /* All ready, let's run the translator! */
    1218                 :            :   rc = spawn_and_wait(stapargv, "/dev/null", stapstdout.c_str (), stapstderr.c_str (),
    1219 [ +  - ][ +  - ]:         35 :                       requestDirName.c_str (), envVec);
         [ +  - ][ +  - ]
    1220                 :            : 
    1221                 :            :   /* Save the RC */
    1222         [ +  - ]:         35 :   string staprc = responseDirName + "/rc";
    1223 [ +  - ][ +  - ]:         35 :   f = fopen(staprc.c_str (), "w");
    1224         [ +  - ]:         35 :   if (f) 
    1225                 :            :     {
    1226                 :            :       /* best effort basis */
    1227         [ +  - ]:         35 :       fprintf(f, "%d", rc);
    1228         [ +  - ]:         35 :       fclose(f);
    1229                 :            :     }
    1230                 :            : 
    1231                 :            :   // In unprivileged modes, if we have a module built, we need to sign the sucker.
    1232         [ +  - ]:         35 :   privilege_t privilege = getRequestedPrivilege (stapargv);
    1233 [ +  - ][ +  + ]:         35 :   if (pr_contains (privilege, pr_stapusr) || pr_contains (privilege, pr_stapsys))
         [ +  - ][ +  + ]
                 [ +  + ]
    1234                 :            :     {
    1235                 :            :       glob_t globber;
    1236                 :            :       char pattern[PATH_MAX];
    1237         [ +  - ]:          3 :       snprintf (pattern, PATH_MAX, "%s/*.ko", new_staptmpdir.c_str());
    1238                 :          3 :       rc = glob (pattern, GLOB_ERR, NULL, &globber);
    1239         [ +  - ]:          3 :       if (rc)
    1240 [ +  - ][ +  - ]:          3 :         server_error (_F("Unable to find a module in %s", new_staptmpdir.c_str()));
         [ +  - ][ +  - ]
    1241         [ #  # ]:          0 :       else if (globber.gl_pathc != 1)
    1242 [ #  # ][ #  # ]:          0 :         server_error (_F("Too many modules (%zu) in %s", globber.gl_pathc, new_staptmpdir.c_str()));
         [ #  # ][ #  # ]
    1243                 :            :       else
    1244                 :            :         {
    1245                 :            :          sign_file (cert_db_path, server_cert_nickname(),
    1246 [ #  # ][ #  # ]:          3 :                     globber.gl_pathv[0], string(globber.gl_pathv[0]) + ".sgn");
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
    1247                 :            :         }
    1248                 :            :     }
    1249                 :            : 
    1250                 :            :   /* If uprobes.ko is required, it will have been built or cache-copied into
    1251                 :            :    * the temp directory.  We need to pack it into the response where the client
    1252                 :            :    * can find it, and sign, if necessary, for unprivileged users.
    1253                 :            :    */
    1254         [ +  - ]:         35 :   string uprobes_ko = new_staptmpdir + "/uprobes/uprobes.ko";
    1255 [ +  - ][ -  + ]:         35 :   if (get_file_size(uprobes_ko) > 0)
    1256                 :            :     {
    1257                 :            :       /* uprobes.ko is required.
    1258                 :            :        *
    1259                 :            :        * It's already underneath the stap tmpdir, but older stap clients
    1260                 :            :        * don't know to look for it there, so, for these clients, we end up packing uprobes twice
    1261                 :            :        * into the zip.  We could move instead of symlink.
    1262                 :            :        */
    1263         [ #  # ]:          0 :       string uprobes_response;
    1264 [ #  # ][ #  # ]:          0 :       if (client_version < "1.6")
         [ #  # ][ #  # ]
    1265                 :            :         {
    1266 [ #  # ][ #  # ]:          0 :           uprobes_response = (string)responseDirName + "/uprobes.ko";
         [ #  # ][ #  # ]
                 [ #  # ]
    1267 [ #  # ][ #  # ]:          0 :           rc = symlink(uprobes_ko.c_str(), uprobes_response.c_str());
    1268         [ #  # ]:          0 :           if (rc != 0)
    1269                 :          0 :             server_error (_F("Could not link to %s from %s",
    1270 [ #  # ][ #  # ]:          0 :                              uprobes_ko.c_str(), uprobes_response.c_str()));
         [ #  # ][ #  # ]
                 [ #  # ]
    1271                 :            :         }
    1272                 :            :       else
    1273         [ #  # ]:          0 :         uprobes_response = uprobes_ko;
    1274                 :            : 
    1275                 :            :       /* In unprivileged mode, we need a signature on uprobes as well. */
    1276 [ #  # ][ #  # ]:          0 :       if (! pr_contains (privilege, pr_stapdev))
    1277                 :            :         {
    1278                 :            :           sign_file (cert_db_path, server_cert_nickname(),
    1279 [ #  # ][ #  # ]:          0 :                      uprobes_response, uprobes_response + ".sgn");
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
    1280         [ #  # ]:          0 :         }
    1281                 :            : 
    1282                 :            :     }
    1283                 :            : 
    1284                 :            :   /* Free up all the arg string copies.  Note that the first few were alloc'd
    1285                 :            :      by wordexp(), which wordfree() frees; others were hand-set to literal strings. */
    1286                 :         35 :   wordfree (& words);
    1287                 :            : 
    1288                 :            :   // Filter paths prefixed with the server's home directory from the stdout and stderr
    1289                 :            :   // files in the response.
    1290         [ +  - ]:         35 :   filter_response_file (stapstdout, responseDirName);
    1291 [ +  - ][ +  - ]:         35 :   filter_response_file (stapstderr, responseDirName);
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
         [ +  - ][ -  + ]
         [ +  - ][ -  + ]
         [ +  - ][ -  + ]
         [ +  - ][ -  + ]
         [ +  - ][ +  - ]
    1292                 :            : 
    1293                 :            :   /* Sorry about the inconvenience.  C string/file processing is such a pleasure. */
    1294                 :            : }
    1295                 :            : 
    1296                 :            : 
    1297                 :            : /* A front end for stap_spawn that handles stdin, stdout, stderr, switches to a working
    1298                 :            :    directory and returns overall success or failure. */
    1299                 :            : static PRStatus
    1300                 :         70 : spawn_and_wait (const vector<string> &argv,
    1301                 :            :                 const char* fd0, const char* fd1, const char* fd2,
    1302                 :            :                 const char *pwd, const vector<string>& envVec)
    1303                 :            : { 
    1304                 :            :   pid_t pid;
    1305                 :            :   int rc;
    1306                 :            :   posix_spawn_file_actions_t actions;
    1307                 :         70 :   int dotfd = -1;
    1308                 :            : 
    1309                 :            : #define CHECKRC(msg) do { if (rc) { server_error (_(msg)); return PR_FAILURE; } } while (0)
    1310                 :            : 
    1311                 :         70 :   rc = posix_spawn_file_actions_init (& actions);
    1312 [ -  + ][ #  # ]:         70 :   CHECKRC ("Error in spawn file actions ctor");
         [ #  # ][ #  # ]
    1313         [ +  + ]:         70 :   if (fd0) {
    1314                 :         35 :     rc = posix_spawn_file_actions_addopen(& actions, 0, fd0, O_RDONLY, 0600);
    1315 [ -  + ][ #  # ]:         35 :     CHECKRC ("Error in spawn file actions fd0");
         [ #  # ][ #  # ]
    1316                 :            :   }
    1317         [ +  + ]:         70 :   if (fd1) {
    1318                 :         35 :     rc = posix_spawn_file_actions_addopen(& actions, 1, fd1, O_WRONLY|O_CREAT, 0600);
    1319 [ -  + ][ #  # ]:         35 :     CHECKRC ("Error in spawn file actions fd1");
         [ #  # ][ #  # ]
    1320                 :            :   }
    1321         [ +  + ]:         70 :   if (fd2) { 
    1322                 :            :     // Use append mode for stderr because it gets written to in other places in the server.
    1323                 :         35 :     rc = posix_spawn_file_actions_addopen(& actions, 2, fd2, O_WRONLY|O_APPEND|O_CREAT, 0600);
    1324 [ -  + ][ #  # ]:         35 :     CHECKRC ("Error in spawn file actions fd2");
         [ #  # ][ #  # ]
    1325                 :            :   }
    1326                 :            : 
    1327                 :            :   /* change temporarily to a directory if requested */
    1328         [ +  - ]:         70 :   if (pwd)
    1329                 :            :     {
    1330         [ +  - ]:         70 :       dotfd = open (".", O_RDONLY);
    1331         [ -  + ]:         70 :       if (dotfd < 0)
    1332                 :            :         {
    1333 [ #  # ][ #  # ]:          0 :           server_error (_("Error in spawn getcwd"));
                 [ #  # ]
    1334                 :          0 :           return PR_FAILURE;
    1335                 :            :         }
    1336                 :            : 
    1337                 :         70 :       rc = chdir (pwd);
    1338         [ -  + ]:         70 :       if (rc)
    1339                 :            :         {
    1340         [ #  # ]:          0 :           close(dotfd);
    1341 [ #  # ][ #  # ]:          0 :           server_error(_("Error in spawn chdir"));
                 [ #  # ]
    1342                 :          0 :           return PR_FAILURE;
    1343                 :            :         }
    1344                 :            :     }
    1345                 :            : 
    1346         [ +  - ]:         70 :   pid = stap_spawn (0, argv, & actions, envVec);
    1347                 :            :   /* NB: don't react to pid==-1 right away; need to chdir back first. */
    1348                 :            : 
    1349 [ +  - ][ +  - ]:         70 :   if (pwd && dotfd >= 0)
    1350                 :            :     {
    1351                 :            :       int subrc;
    1352                 :         70 :       subrc = fchdir (dotfd);
    1353         [ +  - ]:         70 :       subrc |= close (dotfd);
    1354         [ -  + ]:         70 :       if (subrc) 
    1355 [ #  # ][ #  # ]:          0 :         server_error (_("Error in spawn unchdir"));
                 [ #  # ]
    1356                 :            :     }
    1357                 :            : 
    1358         [ -  + ]:         70 :   if (pid == -1)
    1359                 :            :     {
    1360 [ #  # ][ #  # ]:          0 :       server_error (_F("Error in spawn: %s", strerror (errno)));
                 [ #  # ]
    1361                 :          0 :       return PR_FAILURE;
    1362                 :            :     }
    1363                 :            : 
    1364         [ +  - ]:         70 :   rc = stap_waitpid (0, pid);
    1365         [ -  + ]:         70 :   if (rc == -1)
    1366                 :            :     {
    1367 [ #  # ][ #  # ]:          0 :       server_error (_("Error in waitpid"));
                 [ #  # ]
    1368                 :          0 :       return PR_FAILURE;
    1369                 :            :     }
    1370                 :            : 
    1371                 :         70 :   rc = posix_spawn_file_actions_destroy (&actions);
    1372 [ -  + ][ #  # ]:         70 :   CHECKRC ("Error in spawn file actions dtor");
         [ #  # ][ #  # ]
    1373                 :            : 
    1374                 :         70 :   return PR_SUCCESS;
    1375                 :            : #undef CHECKRC
    1376                 :            : }
    1377                 :            : 
    1378                 :            : /* Function:  void *handle_connection()
    1379                 :            :  *
    1380                 :            :  * Purpose: Handle a connection to a socket.  Copy in request zip
    1381                 :            :  * file, process it, copy out response.  Temporary directories are
    1382                 :            :  * created & destroyed here.
    1383                 :            :  */
    1384                 :            : 
    1385                 :            : void *
    1386                 :         36 : handle_connection (void *arg)
    1387                 :            : {
    1388                 :         36 :   PRFileDesc *       sslSocket = NULL;
    1389                 :         36 :   SECStatus          secStatus = SECFailure;
    1390                 :            :   PRStatus           prStatus;
    1391                 :            :   int                rc;
    1392                 :            :   char              *rc1;
    1393                 :            :   char               tmpdir[PATH_MAX];
    1394                 :            :   char               requestFileName[PATH_MAX];
    1395                 :            :   char               requestDirName[PATH_MAX];
    1396                 :            :   char               responseDirName[PATH_MAX];
    1397                 :            :   char               responseFileName[PATH_MAX];
    1398         [ +  - ]:         36 :   string stapstderr; /* Cannot be global since we need a unique
    1399                 :            :                         copy for each connection.*/
    1400         [ +  - ]:         36 :   vector<string>     argv;
    1401                 :            :   PRInt32            bytesRead;
    1402                 :            : 
    1403                 :            :   /* Detatch to avoid a memory leak */
    1404         [ +  - ]:         36 :   if(max_threads > 0)
    1405                 :         36 :     pthread_detach(pthread_self());
    1406                 :            : 
    1407                 :            :   /* Unpack the arg */
    1408                 :         36 :   thread_arg *t_arg = (thread_arg *) arg;
    1409                 :         36 :   PRFileDesc *tcpSocket = t_arg->tcpSocket;
    1410                 :         36 :   CERTCertificate *cert = t_arg->cert;
    1411                 :         36 :   SECKEYPrivateKey *privKey = t_arg->privKey;
    1412                 :         36 :   PRNetAddr addr = t_arg->addr;
    1413                 :            : 
    1414                 :         36 :   tmpdir[0]='\0'; /* prevent cleanup-time /bin/rm of uninitialized directory */
    1415                 :            : 
    1416                 :            : #if 0 // already done on the listenSocket
    1417                 :            :   /* Make sure the socket is blocking. */
    1418                 :            :   PRSocketOptionData socketOption;
    1419                 :            :   socketOption.option             = PR_SockOpt_Nonblocking;
    1420                 :            :   socketOption.value.non_blocking = PR_FALSE;
    1421                 :            :   PR_SetSocketOption (tcpSocket, &socketOption);
    1422                 :            : #endif
    1423                 :         36 :   secStatus = SECFailure;
    1424         [ +  - ]:         36 :   sslSocket = setupSSLSocket (tcpSocket, cert, privKey);
    1425         [ -  + ]:         36 :   if (sslSocket == NULL)
    1426                 :            :     {
    1427                 :            :       // Message already issued.
    1428                 :          0 :       goto cleanup;
    1429                 :            :     }
    1430                 :            : 
    1431         [ +  - ]:         36 :   secStatus = SSL_ResetHandshake(sslSocket, /* asServer */ PR_TRUE);
    1432         [ -  + ]:         35 :   if (secStatus != SECSuccess)
    1433                 :            :     {
    1434 [ #  # ][ #  # ]:          0 :       server_error (_("Error resetting SSL handshake"));
                 [ #  # ]
    1435         [ #  # ]:          0 :       nssError ();
    1436                 :          0 :       goto cleanup;
    1437                 :            :     }
    1438                 :            : 
    1439                 :            : #if 0 // The client authenticates the server, so the client initiates the handshake
    1440                 :            :   /* Force the handshake to complete before moving on. */
    1441                 :            :   secStatus = SSL_ForceHandshake(sslSocket);
    1442                 :            :   if (secStatus != SECSuccess)
    1443                 :            :     {
    1444                 :            :       server_error (_("Error forcing SSL handshake"));
    1445                 :            :       nssError ();
    1446                 :            :       goto cleanup;
    1447                 :            :     }
    1448                 :            : #endif
    1449                 :            : 
    1450                 :         35 :   secStatus = SECFailure;
    1451         [ -  + ]:         35 :   snprintf(tmpdir, PATH_MAX, "%s/stap-server.XXXXXX", getenv("TMPDIR") ?: "/tmp");
    1452                 :         36 :   rc1 = mkdtemp(tmpdir);
    1453         [ -  + ]:         36 :   if (! rc1)
    1454                 :            :     {
    1455 [ #  # ][ #  # ]:          0 :       server_error (_F("Could not create temporary directory %s: %s", tmpdir, strerror(errno)));
                 [ #  # ]
    1456                 :          0 :       tmpdir[0]=0; /* prevent /bin/rm */
    1457                 :          0 :       goto cleanup;
    1458                 :            :     }
    1459                 :            : 
    1460                 :            :   /* Create a temporary files names and directories.  */
    1461                 :         36 :   snprintf (requestFileName, PATH_MAX, "%s/request.zip", tmpdir);
    1462                 :            : 
    1463                 :         36 :   snprintf (requestDirName, PATH_MAX, "%s/request", tmpdir);
    1464                 :         36 :   rc = mkdir(requestDirName, 0700);
    1465         [ -  + ]:         35 :   if (rc)
    1466                 :            :     {
    1467 [ #  # ][ #  # ]:          0 :       server_error (_F("Could not create temporary directory %s: %s", requestDirName, strerror (errno)));
                 [ #  # ]
    1468                 :          0 :       goto cleanup;
    1469                 :            :     }
    1470                 :            : 
    1471                 :         35 :   snprintf (responseDirName, PATH_MAX, "%s/response", tmpdir);
    1472                 :         35 :   rc = mkdir(responseDirName, 0700);
    1473         [ -  + ]:         36 :   if (rc)
    1474                 :            :     {
    1475 [ #  # ][ #  # ]:          0 :       server_error (_F("Could not create temporary directory %s: %s", responseDirName, strerror (errno)));
                 [ #  # ]
    1476                 :          0 :       goto cleanup;
    1477                 :            :     }
    1478                 :            :   // Set this early, since it gets used for errors to be returned to the client.
    1479 [ +  - ][ +  - ]:         36 :   stapstderr = string(responseDirName) + "/stderr";
         [ +  - ][ +  - ]
                 [ +  - ]
    1480                 :            : 
    1481                 :         36 :   snprintf (responseFileName, PATH_MAX, "%s/response.zip", tmpdir);
    1482                 :            : 
    1483                 :            :   /* Read data from the socket.
    1484                 :            :    * If the user is requesting/requiring authentication, authenticate
    1485                 :            :    * the socket.  */
    1486         [ +  - ]:         36 :   bytesRead = readDataFromSocket(sslSocket, requestFileName);
    1487         [ -  + ]:         35 :   if (bytesRead < 0) // Error
    1488                 :          0 :     goto cleanup;
    1489         [ +  + ]:         35 :   if (bytesRead == 0) // No request -- not an error
    1490                 :            :     {
    1491                 :          1 :       secStatus = SECSuccess;
    1492                 :          1 :       goto cleanup;
    1493                 :            :     }
    1494                 :            : 
    1495                 :            : #if 0 /* Don't authenticate after each transaction */
    1496                 :            :   if (REQUEST_CERT_ALL)
    1497                 :            :     {
    1498                 :            :       secStatus = authenticateSocket(sslSocket);
    1499                 :            :       if (secStatus != SECSuccess)
    1500                 :            :         goto cleanup;
    1501                 :            :     }
    1502                 :            : #endif
    1503                 :            : 
    1504                 :            :   /* Unzip the request. */
    1505                 :         34 :   secStatus = SECFailure;
    1506 [ +  - ][ +  - ]:         34 :   argv.push_back ("unzip");
                 [ +  - ]
    1507 [ +  - ][ +  - ]:         35 :   argv.push_back ("-q");
                 [ +  - ]
    1508 [ +  - ][ +  - ]:         35 :   argv.push_back ("-d");
                 [ +  - ]
    1509 [ +  - ][ +  - ]:         35 :   argv.push_back (requestDirName);
                 [ +  - ]
    1510 [ +  - ][ +  - ]:         35 :   argv.push_back (requestFileName);
                 [ +  - ]
    1511         [ +  - ]:         35 :   rc = stap_system (0, argv);
    1512         [ -  + ]:         35 :   if (rc != 0)
    1513                 :            :     {
    1514 [ #  # ][ #  # ]:          0 :       server_error (_("Unable to extract client request"));
                 [ #  # ]
    1515                 :          0 :       goto cleanup;
    1516                 :            :     }
    1517                 :            : 
    1518                 :            :   /* Handle the request zip file.  An error therein should still result
    1519                 :            :      in a response zip file (containing stderr etc.) so we don't have to
    1520                 :            :      have a result code here.  */
    1521 [ +  - ][ +  - ]:         35 :   handleRequest(requestDirName, responseDirName, stapstderr);
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
                 [ +  - ]
    1522                 :            : 
    1523                 :            :   /* Zip the response. */
    1524         [ +  - ]:         35 :   argv.clear ();
    1525 [ +  - ][ +  - ]:         35 :   argv.push_back ("zip");
                 [ +  - ]
    1526 [ +  - ][ +  - ]:         35 :   argv.push_back ("-q");
                 [ +  - ]
    1527 [ +  - ][ +  - ]:         35 :   argv.push_back ("-r");
                 [ +  - ]
    1528 [ +  - ][ +  - ]:         35 :   argv.push_back (responseFileName);
                 [ +  - ]
    1529 [ +  - ][ +  - ]:         35 :   argv.push_back (".");
                 [ +  - ]
    1530 [ +  - ][ +  - ]:         35 :   rc = spawn_and_wait (argv, NULL, NULL, NULL, responseDirName);
                 [ +  - ]
    1531         [ -  + ]:         35 :   if (rc != PR_SUCCESS)
    1532                 :            :     {
    1533 [ #  # ][ #  # ]:          0 :       server_error (_("Unable to compress server response"));
                 [ #  # ]
    1534                 :          0 :       goto cleanup;
    1535                 :            :     }
    1536                 :            : 
    1537         [ +  - ]:         35 :   secStatus = writeDataToSocket (sslSocket, responseFileName);
    1538                 :            : 
    1539                 :            : cleanup:
    1540         [ +  - ]:         36 :   if (sslSocket)
    1541 [ +  - ][ -  + ]:         36 :     if (PR_Close (sslSocket) != PR_SUCCESS)
    1542                 :            :       {
    1543 [ #  # ][ #  # ]:          0 :         server_error (_("Error closing ssl socket"));
                 [ #  # ]
    1544         [ #  # ]:          0 :         nssError ();
    1545                 :            :       }
    1546                 :            : 
    1547         [ +  - ]:         36 :   if (tmpdir[0]) 
    1548                 :            :     {
    1549                 :            :       // Remove the whole tmpdir and all that lies beneath, unless -k was specified.
    1550         [ -  + ]:         36 :       if (keep_temp) 
    1551 [ #  # ][ #  # ]:          0 :         log (_F("Keeping temporary directory %s", tmpdir));
                 [ #  # ]
    1552                 :            :       else
    1553                 :            :         {
    1554         [ +  - ]:         36 :           argv.clear ();
    1555 [ +  - ][ +  - ]:         36 :           argv.push_back ("rm");
                 [ +  - ]
    1556 [ +  - ][ +  - ]:         36 :           argv.push_back ("-r");
                 [ +  - ]
    1557 [ +  - ][ +  - ]:         36 :           argv.push_back (tmpdir);
                 [ +  - ]
    1558         [ +  - ]:         36 :           rc = stap_system (0, argv);
    1559         [ -  + ]:         36 :           if (rc != 0)
    1560 [ #  # ][ #  # ]:          0 :             server_error (_("Error in tmpdir cleanup"));
                 [ #  # ]
    1561                 :            :         }
    1562                 :            :     }
    1563                 :            : 
    1564         [ -  + ]:         36 :   if (secStatus != SECSuccess)
    1565 [ #  # ][ #  # ]:          0 :     server_error (_("Error processing client request"));
                 [ #  # ]
    1566                 :            : 
    1567                 :            :   // Log the end of the request.
    1568                 :            :   char buf[1024];
    1569         [ +  - ]:         36 :   prStatus = PR_NetAddrToString (& addr, buf, sizeof (buf));
    1570         [ +  - ]:         36 :   if (prStatus == PR_SUCCESS)
    1571                 :            :     {
    1572         [ -  + ]:         36 :       if (addr.raw.family == PR_AF_INET)
    1573 [ #  # ][ #  # ]:          0 :         log (_F("Request from %s:%d complete", buf, addr.inet.port));
                 [ #  # ]
    1574         [ +  - ]:         36 :       else if (addr.raw.family == PR_AF_INET6)
    1575 [ +  - ][ +  - ]:         36 :         log (_F("Request from [%s]:%d complete", buf, addr.ipv6.port));
                 [ +  - ]
    1576                 :            :     }
    1577                 :            : 
    1578                 :            :   /* Increment semephore to indicate this thread is finished. */
    1579                 :         36 :   free(t_arg);
    1580         [ +  - ]:         36 :   if (max_threads > 0)
    1581                 :            :     {
    1582                 :         36 :       sem_post(&sem_client);
    1583                 :         36 :       pthread_exit(0);
    1584                 :            :     }
    1585                 :            :   else
    1586 [ #  # ][ #  # ]:         36 :     return 0;
    1587                 :            : }
    1588                 :            : 
    1589                 :            : /* Function:  int accept_connection()
    1590                 :            :  *
    1591                 :            :  * Purpose:  Accept a connection to the socket.
    1592                 :            :  *
    1593                 :            :  */
    1594                 :            : static SECStatus
    1595                 :          5 : accept_connections (PRFileDesc *listenSocket, CERTCertificate *cert)
    1596                 :            : {
    1597                 :            :   PRNetAddr   addr;
    1598                 :            :   PRFileDesc *tcpSocket;
    1599                 :            :   PRStatus    prStatus;
    1600                 :            :   SECStatus   secStatus;
    1601                 :            :   CERTCertDBHandle *dbHandle;
    1602                 :            :   pthread_t tid;
    1603                 :            :   thread_arg *t_arg;
    1604                 :            : 
    1605                 :            : 
    1606         [ +  - ]:          5 :   dbHandle = CERT_GetDefaultCertDB ();
    1607                 :            : 
    1608                 :            :   // cert_db_path gets passed to nssPasswordCallback.
    1609 [ +  - ][ +  - ]:          5 :   SECKEYPrivateKey *privKey = PK11_FindKeyByAnyCert (cert, (void*)cert_db_path.c_str ());
    1610         [ -  + ]:          5 :   if (privKey == NULL)
    1611                 :            :     {
    1612 [ #  # ][ #  # ]:          0 :       server_error (_("Unable to obtain certificate private key"));
                 [ #  # ]
    1613         [ #  # ]:          0 :       nssError ();
    1614                 :          0 :       return SECFailure;
    1615                 :            :     }
    1616                 :            : 
    1617         [ +  + ]:        150 :   while (pending_interrupts == 0)
    1618                 :            :     {
    1619                 :            :       /* Accept a connection to the socket. */
    1620         [ +  - ]:        145 :       tcpSocket = PR_Accept (listenSocket, &addr, PR_INTERVAL_MIN);
    1621         [ +  + ]:        145 :       if (tcpSocket == NULL)
    1622                 :            :         {
    1623 [ +  - ][ +  - ]:        109 :           if(PR_GetError() == PR_IO_TIMEOUT_ERROR)
    1624                 :        109 :             continue;
    1625                 :            :           else
    1626                 :            :             {
    1627 [ #  # ][ #  # ]:          0 :               server_error (_("Error accepting client connection"));
                 [ #  # ]
    1628                 :            :               break;
    1629                 :            :             }
    1630                 :            :         }
    1631                 :            : 
    1632                 :            :       /* Log the accepted connection.  */
    1633                 :            :       char buf[1024];
    1634         [ +  - ]:         36 :       prStatus = PR_NetAddrToString (&addr, buf, sizeof (buf));
    1635         [ +  - ]:         36 :       if (prStatus == PR_SUCCESS)
    1636                 :            :         {
    1637         [ -  + ]:         36 :           if (addr.raw.family == PR_AF_INET)
    1638 [ #  # ][ #  # ]:          0 :             log (_F("Accepted connection from %s:%d", buf, addr.inet.port));
                 [ #  # ]
    1639         [ +  - ]:         36 :           else if (addr.raw.family == PR_AF_INET6)
    1640 [ +  - ][ +  - ]:         36 :             log (_F("Accepted connection from [%s]:%d", buf, addr.ipv6.port));
                 [ +  - ]
    1641                 :            :         }
    1642                 :            : 
    1643                 :            :       /* XXX: alarm() or somesuch to set a timeout. */
    1644                 :            : 
    1645                 :            :       /* Accepted the connection, now handle it. */
    1646                 :            : 
    1647                 :            :       /* Wait for a thread to finish if there are none available */
    1648         [ +  - ]:         36 :       if(max_threads >0)
    1649                 :            :         {
    1650                 :            :           int idle_threads;
    1651                 :         36 :           sem_getvalue(&sem_client, &idle_threads);
    1652         [ -  + ]:         36 :           if(idle_threads <= 0)
    1653 [ #  # ][ #  # ]:          0 :             log(_("Server is overloaded. Processing times may be longer than normal."));
                 [ #  # ]
    1654         [ +  + ]:         36 :           else if (idle_threads == max_threads)
    1655 [ +  - ][ +  - ]:         31 :             log(_("Processing 1 request..."));
                 [ +  - ]
    1656                 :            :           else
    1657 [ +  - ][ +  - ]:          5 :             log(_F("Processing %d concurrent requests...", ((int)max_threads - idle_threads) + 1));
                 [ +  - ]
    1658                 :            : 
    1659         [ +  - ]:         36 :           sem_wait(&sem_client);
    1660                 :            :         }
    1661                 :            : 
    1662                 :            :       /* Create the argument structure to pass to pthread_create
    1663                 :            :        * (or directly to handle_connection if max_threads == 0 */
    1664                 :         36 :       t_arg = (thread_arg *)malloc(sizeof(*t_arg));
    1665         [ -  + ]:         36 :       if (t_arg == 0)
    1666 [ #  # ][ #  # ]:          0 :         fatal(_("No memory available for new thread arg!"));
                 [ #  # ]
    1667                 :         36 :       t_arg->tcpSocket = tcpSocket;
    1668                 :         36 :       t_arg->cert = cert;
    1669                 :         36 :       t_arg->privKey = privKey;
    1670                 :         36 :       t_arg->addr = addr;
    1671                 :            : 
    1672                 :            :       /* Handle the conncection */
    1673         [ +  - ]:         36 :       if (max_threads > 0)
    1674                 :            :         /* Create the worker thread and handle the connection. */
    1675                 :         36 :         pthread_create(&tid, NULL, handle_connection, t_arg);
    1676                 :            :       else
    1677                 :            :         /* Since max_threads == 0, don't spawn a new thread,
    1678                 :            :          * just handle in the current thread. */
    1679         [ #  # ]:          0 :         handle_connection(t_arg);
    1680                 :            : 
    1681                 :            :       // If our certificate is no longer valid (e.g. has expired), then exit.
    1682                 :            :       secStatus = CERT_VerifyCertNow (dbHandle, cert, PR_TRUE/*checkSig*/,
    1683         [ +  - ]:         36 :                                       certUsageSSLServer, NULL/*wincx*/);
    1684         [ +  - ]:         36 :       if (secStatus != SECSuccess)
    1685                 :            :         {
    1686                 :            :           // Not an error. Exit the loop so a new cert can be generated.
    1687                 :            :           break;
    1688                 :            :         }
    1689                 :            :     }
    1690                 :            : 
    1691         [ +  - ]:          5 :   SECKEY_DestroyPrivateKey (privKey);
    1692                 :          5 :   return SECSuccess;
    1693                 :            : }
    1694                 :            : 
    1695                 :            : /* Function:  void server_main()
    1696                 :            :  *
    1697                 :            :  * Purpose:  This is the server's main function.  It configures a socket
    1698                 :            :  *                       and listens to it.
    1699                 :            :  *
    1700                 :            :  */
    1701                 :            : static SECStatus
    1702                 :          5 : server_main (PRFileDesc *listenSocket)
    1703                 :            : {
    1704                 :            :   int idle_threads;
    1705                 :          5 :   int timeout = 0;
    1706                 :            : 
    1707                 :            :   // Initialize NSS.
    1708 [ +  - ][ +  - ]:          5 :   SECStatus secStatus = nssInit (cert_db_path.c_str ());
    1709         [ -  + ]:          5 :   if (secStatus != SECSuccess)
    1710                 :            :     {
    1711                 :            :       // Message already issued.
    1712                 :          0 :       return secStatus;
    1713                 :            :     }
    1714                 :            : 
    1715                 :            :   // Preinitialized here due to jumps to the label 'done'.
    1716                 :          5 :   CERTCertificate *cert = NULL;
    1717                 :          5 :   bool serverCacheConfigured = false;
    1718                 :            : 
    1719                 :            :   // Enable cipher suites which are allowed by U.S. export regulations.
    1720                 :            :   // NB: The NSS docs say that SSL_ClearSessionCache is required for the new settings to take
    1721                 :            :   // effect, however, calling it puts NSS in a state where it will not shut down cleanly.
    1722                 :            :   // We need to be able to shut down NSS cleanly if we are to generate a new certificate when
    1723                 :            :   // ours expires. It should be noted however, thet SSL_ClearSessionCache only clears the
    1724                 :            :   // client cache, and we are a server.
    1725         [ +  - ]:          5 :   secStatus = NSS_SetExportPolicy ();
    1726                 :            :   //      SSL_ClearSessionCache ();
    1727         [ -  + ]:          5 :   if (secStatus != SECSuccess)
    1728                 :            :     {
    1729 [ #  # ][ #  # ]:          0 :       server_error (_("Unable to set NSS export policy"));
                 [ #  # ]
    1730         [ #  # ]:          0 :       nssError ();
    1731                 :          0 :       goto done;
    1732                 :            :     }
    1733                 :            : 
    1734                 :            :   // Configure the SSL session cache for a single process server with the default settings.
    1735         [ +  - ]:          5 :   secStatus = SSL_ConfigServerSessionIDCache (0, 0, 0, NULL);
    1736         [ -  + ]:          5 :   if (secStatus != SECSuccess)
    1737                 :            :     {
    1738 [ #  # ][ #  # ]:          0 :       server_error (_("Unable to configure SSL server session ID cache"));
                 [ #  # ]
    1739         [ #  # ]:          0 :       nssError ();
    1740                 :          0 :       goto done;
    1741                 :            :     }
    1742                 :          5 :   serverCacheConfigured = true;
    1743                 :            : 
    1744                 :            :   /* Get own certificate. */
    1745 [ +  - ][ +  - ]:          5 :   cert = PK11_FindCertFromNickname (server_cert_nickname (), NULL);
    1746         [ -  + ]:          5 :   if (cert == NULL)
    1747                 :            :     {
    1748                 :          0 :       server_error (_F("Unable to find our certificate in the database at %s", 
    1749 [ #  # ][ #  # ]:          0 :                           cert_db_path.c_str ()));
         [ #  # ][ #  # ]
    1750         [ #  # ]:          0 :       nssError ();
    1751                 :          0 :       goto done;
    1752                 :            :     }
    1753                 :            : 
    1754                 :            :   // Tell the world that we're listening.
    1755         [ +  - ]:          5 :   advertise_presence (cert);
    1756                 :            : 
    1757                 :            :   /* Handle connections to the socket. */
    1758         [ +  - ]:          5 :   secStatus = accept_connections (listenSocket, cert);
    1759                 :            : 
    1760                 :            :   // Tell the world we're no longer listening.
    1761         [ +  - ]:          5 :   unadvertise_presence ();
    1762                 :            : 
    1763                 :          5 :   sem_getvalue(&sem_client, &idle_threads);
    1764                 :            : 
    1765                 :            :   /* Wait for requests to finish or the timeout to be reached.
    1766                 :            :    * If we got here from an interrupt, exit immediately if
    1767                 :            :    * the timeout is reached. Otherwise, wait indefinitiely
    1768                 :            :    * until the threads exit (or an interrupt is recieved).*/
    1769         [ -  + ]:          5 :   if(idle_threads < max_threads)
    1770 [ #  # ][ #  # ]:          0 :     log(_F("Waiting for %d outstanding requests to complete...", (int)max_threads - idle_threads));
                 [ #  # ]
    1771         [ -  + ]:          5 :   while(idle_threads < max_threads)
    1772                 :            :     {
    1773 [ #  # ][ #  # ]:          0 :       if(pending_interrupts && timeout++ > CONCURRENCY_TIMEOUT_S)
                 [ #  # ]
    1774                 :            :         {
    1775 [ #  # ][ #  # ]:          0 :           log(_("Timeout reached, exiting (forced)"));
                 [ #  # ]
    1776         [ #  # ]:          0 :           kill_stap_spawn (SIGTERM);
    1777         [ #  # ]:          0 :           cleanup ();
    1778                 :          0 :           _exit(0);
    1779                 :            :         }
    1780         [ #  # ]:          0 :       sleep(1);
    1781                 :          0 :       sem_getvalue(&sem_client, &idle_threads);
    1782                 :            :     }
    1783                 :            : 
    1784                 :            :  done:
    1785                 :            :   // Clean up
    1786         [ +  - ]:          5 :   if (cert)
    1787         [ +  - ]:          5 :     CERT_DestroyCertificate (cert);
    1788                 :            : 
    1789                 :            :   // Shutdown NSS
    1790 [ +  - ][ +  - ]:          5 :   if (serverCacheConfigured && SSL_ShutdownServerSessionIDCache () != SECSuccess)
         [ -  + ][ -  + ]
    1791                 :            :     {
    1792 [ #  # ][ #  # ]:          0 :       server_error (_("Unable to shut down server session ID cache"));
                 [ #  # ]
    1793         [ #  # ]:          0 :       nssError ();
    1794                 :            :     }
    1795 [ +  - ][ +  - ]:          5 :   nssCleanup (cert_db_path.c_str ());
    1796                 :            : 
    1797                 :          5 :   return secStatus;
    1798                 :            : }
    1799                 :            : 
    1800                 :            : static void
    1801                 :          5 : listen ()
    1802                 :            : {
    1803                 :            :   // Create a new socket.
    1804         [ +  - ]:          5 :   PRFileDesc *listenSocket = PR_OpenTCPSocket (PR_AF_INET6); // Accepts IPv4 too
    1805         [ -  + ]:          5 :   if (listenSocket == NULL)
    1806                 :            :     {
    1807 [ #  # ][ #  # ]:          0 :       server_error (_("Error creating socket"));
                 [ #  # ]
    1808         [ #  # ]:          0 :       nssError ();
    1809                 :          5 :       return;
    1810                 :            :     }
    1811                 :            : 
    1812                 :            :   // Set socket to be blocking - on some platforms the default is nonblocking.
    1813                 :            :   PRSocketOptionData socketOption;
    1814                 :          5 :   socketOption.option = PR_SockOpt_Nonblocking;
    1815                 :          5 :   socketOption.value.non_blocking = PR_FALSE;
    1816         [ +  - ]:          5 :   PRStatus prStatus = PR_SetSocketOption (listenSocket, & socketOption);
    1817         [ -  + ]:          5 :   if (prStatus != PR_SUCCESS)
    1818                 :            :     {
    1819 [ #  # ][ #  # ]:          0 :       server_error (_("Error setting socket properties"));
                 [ #  # ]
    1820         [ #  # ]:          0 :       nssError ();
    1821                 :          0 :       goto done;
    1822                 :            :     }
    1823                 :            : 
    1824                 :            :   // Allow the socket address to be reused, in case we want the same port across a
    1825                 :            :   // 'service stap-server restart'
    1826                 :          5 :   socketOption.option = PR_SockOpt_Reuseaddr;
    1827                 :          5 :   socketOption.value.reuse_addr = PR_TRUE;
    1828         [ +  - ]:          5 :   prStatus = PR_SetSocketOption (listenSocket, & socketOption);
    1829         [ -  + ]:          5 :   if (prStatus != PR_SUCCESS)
    1830                 :            :     {
    1831 [ #  # ][ #  # ]:          0 :       server_error (_("Error setting socket properties"));
                 [ #  # ]
    1832         [ #  # ]:          0 :       nssError ();
    1833                 :          0 :       goto done;
    1834                 :            :     }
    1835                 :            : 
    1836                 :            :   // Configure the network connection.
    1837                 :            :   PRNetAddr addr;
    1838                 :          5 :   memset (& addr, 0, sizeof(addr));
    1839         [ +  - ]:          5 :   prStatus = PR_InitializeNetAddr (PR_IpAddrAny, port, & addr);
    1840                 :          5 :   addr.ipv6.family = PR_AF_INET6;
    1841                 :            : #if 0
    1842                 :            :   // addr.inet.ip = PR_htonl(PR_INADDR_ANY);
    1843                 :            :   PR_StringToNetAddr ("::", & addr);
    1844                 :            :   // PR_StringToNetAddr ("fe80::5eff:35ff:fe07:55ca", & addr);
    1845                 :            :   // PR_StringToNetAddr ("::1", & addr);
    1846                 :            :   addr.ipv6.port = PR_htons (port);
    1847                 :            : #endif
    1848                 :            : 
    1849                 :            :   // Bind the socket to an address. Retry if the selected port is busy, unless the port was
    1850                 :            :   // specified directly.
    1851                 :          0 :   for (;;)
    1852                 :            :     {
    1853                 :            :       /* Bind the address to the listener socket. */
    1854         [ +  - ]:          5 :       prStatus = PR_Bind (listenSocket, & addr);
    1855         [ +  - ]:          5 :       if (prStatus == PR_SUCCESS)
    1856                 :          5 :         break;
    1857                 :            : 
    1858                 :            :       // If the selected port is busy. Try another, but only if a specific port was not specified.
    1859         [ #  # ]:          0 :       PRErrorCode errorNumber = PR_GetError ();
    1860      [ #  #  # ]:          0 :       switch (errorNumber)
    1861                 :            :         {
    1862                 :            :         case PR_ADDRESS_NOT_AVAILABLE_ERROR:
    1863         [ #  # ]:          0 :           if (port == 0)
    1864                 :            :             {
    1865 [ #  # ][ #  # ]:          0 :               server_error (_F("Network port %hu is unavailable. Trying another port", port));
                 [ #  # ]
    1866                 :          0 :               continue;
    1867                 :            :             }
    1868                 :          0 :           break;
    1869                 :            :         case PR_ADDRESS_IN_USE_ERROR:
    1870         [ #  # ]:          0 :           if (port == 0)
    1871                 :            :             {
    1872 [ #  # ][ #  # ]:          0 :               server_error (_F("Network port %hu is busy. Trying another port", port));
                 [ #  # ]
    1873                 :          0 :               continue;
    1874                 :            :             }
    1875                 :          0 :           break;
    1876                 :            :         default:
    1877                 :          0 :           break;
    1878                 :            :         }
    1879 [ #  # ][ #  # ]:          0 :       server_error (_("Error setting socket address"));
                 [ #  # ]
    1880         [ #  # ]:          0 :       nssError ();
    1881                 :          0 :       goto done;
    1882                 :            :     }
    1883                 :            : 
    1884                 :            :   // Query the socket for the port that was assigned.
    1885         [ +  - ]:          5 :   prStatus = PR_GetSockName (listenSocket, &addr);
    1886         [ -  + ]:          5 :   if (prStatus != PR_SUCCESS)
    1887                 :            :     {
    1888 [ #  # ][ #  # ]:          0 :       server_error (_("Unable to obtain socket address"));
                 [ #  # ]
    1889         [ #  # ]:          0 :       nssError ();
    1890                 :          0 :       goto done;
    1891                 :            :     }
    1892                 :            :   char buf[1024];
    1893         [ +  - ]:          5 :   prStatus = PR_NetAddrToString (&addr, buf, sizeof (buf));
    1894         [ +  - ]:          5 :   port = PR_ntohs (addr.ipv6.port);
    1895 [ +  - ][ +  - ]:          5 :   log (_F("Using network address [%s]:%hu", buf, port));
                 [ +  - ]
    1896                 :            : 
    1897         [ +  - ]:          5 :   if (max_threads > 0)
    1898 [ +  - ][ +  - ]:          5 :     log (_F("Using a maximum of %ld threads", max_threads));
                 [ +  - ]
    1899                 :            :   else
    1900 [ #  # ][ #  # ]:          0 :     log (_("Concurrency disabled"));
                 [ #  # ]
    1901                 :            : 
    1902                 :            :   // Listen for connection on the socket.  The second argument is the maximum size of the queue
    1903                 :            :   // for pending connections.
    1904         [ +  - ]:          5 :   prStatus = PR_Listen (listenSocket, 5);
    1905         [ -  + ]:          5 :   if (prStatus != PR_SUCCESS)
    1906                 :            :     {
    1907 [ #  # ][ #  # ]:          0 :       server_error (_("Error listening on socket"));
                 [ #  # ]
    1908         [ #  # ]:          0 :       nssError ();
    1909                 :          0 :       goto done;
    1910                 :            :     }
    1911                 :            : 
    1912                 :            :   /* Initialize semephore with the maximum number of threads
    1913                 :            :    * defined by --max-threads. If it is not defined, the
    1914                 :            :    * default is the number of processors */
    1915                 :          5 :   sem_init(&sem_client, 0, max_threads);
    1916                 :            : 
    1917                 :            :   // Loop forever. We check our certificate (and regenerate, if necessary) and then start the
    1918                 :            :   // server. The server will go down when our certificate is no longer valid (e.g. expired). We
    1919                 :            :   // then generate a new one and start the server again.
    1920         [ +  + ]:         10 :   while(!pending_interrupts)
    1921                 :            :     {
    1922                 :            :       // Ensure that our certificate is valid. Generate a new one if not.
    1923 [ +  - ][ +  - ]:          5 :       if (check_cert (cert_db_path, server_cert_nickname (), use_db_password) != 0)
         [ +  - ][ +  - ]
                 [ -  + ]
    1924                 :            :         {
    1925                 :            :           // Message already issued
    1926                 :          0 :           goto done;
    1927                 :            :         }
    1928                 :            : 
    1929                 :            :       // Ensure that our certificate is trusted by our local client.
    1930                 :            :       // Construct the client database path relative to the server database path.
    1931                 :            :       SECStatus secStatus = add_client_cert (server_cert_file (),
    1932 [ +  - ][ +  - ]:          5 :                                              local_client_cert_db_path ());
         [ +  - ][ +  - ]
                 [ +  - ]
    1933         [ -  + ]:          5 :       if (secStatus != SECSuccess)
    1934                 :            :         {
    1935 [ #  # ][ #  # ]:          0 :           server_error (_("Unable to authorize certificate for the local client"));
                 [ #  # ]
    1936                 :          0 :           goto done;
    1937                 :            :         }
    1938                 :            : 
    1939                 :            :       // Launch the server.
    1940         [ +  - ]:          5 :       secStatus = server_main (listenSocket);
    1941                 :            :     } // loop forever
    1942                 :            : 
    1943                 :            :  done:
    1944                 :          5 :   sem_destroy(&sem_client); /*Not really necessary, as we are shutting down...but for correctness */
    1945 [ +  - ][ -  + ]:          5 :   if (PR_Close (listenSocket) != PR_SUCCESS)
    1946                 :            :     {
    1947 [ #  # ][ #  # ]:          0 :       server_error (_("Error closing listen socket"));
                 [ #  # ]
    1948         [ #  # ]:          5 :       nssError ();
    1949                 :            :     }
    1950                 :            : }
    1951                 :            : 
    1952                 :            : int
    1953                 :          5 : main (int argc, char **argv) {
    1954                 :          5 :   initialize (argc, argv);
    1955                 :          5 :   listen ();
    1956                 :          5 :   cleanup ();
    1957                 :          5 :   return 0;
    1958 [ +  - ][ +  - ]:         15 : }
    1959                 :            : 
    1960                 :            : /* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */

Generated by: LCOV version 1.9