LCOV - code coverage report
Current view: top level - mnt/wasteland/wcohen/systemtap_write/systemtap - remote.cxx (source / functions) Hit Total Coverage
Test: stap.info Lines: 247 499 49.5 %
Date: 2013-03-08 Functions: 29 47 61.7 %
Branches: 342 1524 22.4 %

           Branch data     Line data    Source code
       1                 :            : // systemtap remote execution
       2                 :            : // Copyright (C) 2010-2011 Red Hat Inc.
       3                 :            : //
       4                 :            : // This file is part of systemtap, and is free software.  You can
       5                 :            : // redistribute it and/or modify it under the terms of the GNU General
       6                 :            : // Public License (GPL); either version 2, or (at your option) any
       7                 :            : // later version.
       8                 :            : 
       9                 :            : #include "config.h"
      10                 :            : 
      11                 :            : extern "C" {
      12                 :            : #include <fcntl.h>
      13                 :            : #include <poll.h>
      14                 :            : #include <sys/types.h>
      15                 :            : #include <sys/stat.h>
      16                 :            : #include <unistd.h>
      17                 :            : #include <sys/socket.h>
      18                 :            : #include <sys/un.h>
      19                 :            : }
      20                 :            : 
      21                 :            : #include <cstdio>
      22                 :            : #include <iomanip>
      23                 :            : #include <memory>
      24                 :            : #include <stdexcept>
      25                 :            : #include <sstream>
      26                 :            : #include <string>
      27                 :            : #include <vector>
      28                 :            : 
      29                 :            : #include "buildrun.h"
      30                 :            : #include "remote.h"
      31                 :            : #include "util.h"
      32                 :            : 
      33                 :            : using namespace std;
      34                 :            : 
      35                 :            : // Decode URIs as per RFC 3986, though not bothering to be strict
      36 [ +  - ][ +  - ]:       2214 : class uri_decoder {
         [ +  - ][ +  - ]
                 [ +  - ]
      37                 :            :   public:
      38                 :            :     const string uri;
      39                 :            :     string scheme, authority, path, query, fragment;
      40                 :            :     bool has_authority, has_query, has_fragment;
      41                 :            : 
      42                 :       2214 :     uri_decoder(const string& uri):
      43 [ +  - ][ +  - ]:       2214 :       uri(uri), has_authority(false), has_query(false), has_fragment(false)
         [ +  - ][ +  - ]
                 [ +  - ]
      44                 :            :     {
      45                 :            :       const string re =
      46         [ +  - ]:       2214 :         "^([^:]+):(//[^/?#]*)?([^?#]*)(\\?[^#]*)?(#.*)?$";
      47                 :            : 
      48         [ +  - ]:       2214 :       vector<string> matches;
      49 [ +  - ][ -  + ]:       2214 :       if (regexp_match(uri, re, matches) != 0)
      50 [ #  # ][ #  # ]:          0 :         throw runtime_error(_F("string doesn't appear to be a URI: %s", uri.c_str()));
                 [ #  # ]
      51                 :            : 
      52         [ +  - ]:       2214 :       scheme = matches[1];
      53                 :            : 
      54 [ +  - ][ -  + ]:       2214 :       if (!matches[2].empty())
      55                 :            :         {
      56                 :          0 :           has_authority = true;
      57 [ #  # ][ #  # ]:          0 :           authority = matches[2].substr(2);
                 [ #  # ]
      58                 :            :         }
      59                 :            : 
      60         [ +  - ]:       2214 :       path = matches[3];
      61                 :            : 
      62 [ +  - ][ -  + ]:       2214 :       if (!matches[4].empty())
      63                 :            :         {
      64                 :          0 :           has_query = true;
      65 [ #  # ][ #  # ]:          0 :           query = matches[4].substr(1);
                 [ #  # ]
      66                 :            :         }
      67                 :            : 
      68 [ +  - ][ -  + ]:       2214 :       if (!matches[5].empty())
      69                 :            :         {
      70                 :          0 :           has_fragment = true;
      71 [ #  # ][ #  # ]:          0 :           fragment = matches[5].substr(1);
                 [ #  # ]
      72 [ +  - ][ +  - ]:       2214 :         }
      73                 :       2214 :     }
      74                 :            : };
      75                 :            : 
      76                 :            : 
      77                 :            : // loopback target for running locally
      78                 :            : class direct : public remote {
      79                 :            :   private:
      80                 :            :     pid_t child;
      81                 :            :     vector<string> args;
      82         [ +  - ]:       2209 :     direct(systemtap_session& s): remote(s), child(0) {}
      83                 :            : 
      84                 :        473 :     int start()
      85                 :            :       {
      86 [ +  - ][ +  - ]:        473 :         args = make_run_command(*s);
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
                 [ +  - ]
      87         [ +  + ]:        473 :         if (! staprun_r_arg.empty()) // PR13354
      88                 :            :           {
      89 [ +  - ][ +  - ]:          2 :             args.push_back ("-r");
                 [ +  - ]
      90                 :          2 :             args.push_back (staprun_r_arg);
      91                 :            :           }
      92                 :        473 :         pid_t pid = stap_spawn (s->verbose, args);
      93         [ -  + ]:        473 :         if (pid <= 0)
      94                 :          0 :           return 1;
      95                 :        473 :         child = pid;
      96                 :        473 :         return 0;
      97                 :            :       }
      98                 :            : 
      99                 :       2673 :     int finish()
     100                 :            :       {
     101         [ +  + ]:       2673 :         if (child <= 0)
     102                 :       2200 :           return 1;
     103                 :            : 
     104                 :        473 :         int ret = stap_waitpid(s->verbose, child);
     105         [ -  + ]:        473 :         if (ret > 128)
     106                 :          0 :           s->print_warning(_F("%s exited with signal: %d (%s)",
     107                 :            :                               args.front().c_str(), ret - 128,
     108         [ #  # ]:          0 :                               strsignal(ret - 128)));
     109         [ +  + ]:        473 :         else if (ret > 0)
     110                 :        101 :           s->print_warning(_F("%s exited with status: %d",
     111         [ +  - ]:        101 :                               args.front().c_str(), ret));
     112                 :        473 :         child = 0;
     113                 :       2673 :         return ret;
     114                 :            :       }
     115                 :            : 
     116                 :            :   public:
     117                 :            :     friend class remote;
     118                 :            : 
     119 [ +  - ][ +  - ]:       4400 :     virtual ~direct() { finish(); }
                 [ -  + ]
     120                 :            : };
     121                 :            : 
     122                 :            : 
     123                 :            : class stapsh : public remote {
     124                 :            :   private:
     125                 :            :     int interrupts_sent;
     126                 :            :     int fdin, fdout;
     127                 :            :     FILE *IN, *OUT;
     128                 :            :     string remote_version;
     129                 :            : 
     130                 :         19 :     virtual void prepare_poll(vector<pollfd>& fds)
     131                 :            :       {
     132 [ +  + ][ +  - ]:         19 :         if (fdout >= 0 && OUT)
     133                 :            :           {
     134                 :         16 :             pollfd p = { fdout, POLLIN, 0 };
     135         [ +  - ]:         16 :             fds.push_back(p);
     136                 :            :           }
     137                 :            : 
     138                 :            :         // need to send a signal?
     139 [ +  + ][ +  - ]:         19 :         if (fdin >= 0 && IN && interrupts_sent < pending_interrupts)
                 [ +  + ]
     140                 :            :           {
     141                 :          3 :             pollfd p = { fdin, POLLOUT, 0 };
     142         [ +  - ]:          3 :             fds.push_back(p);
     143                 :            :           }
     144                 :         19 :       }
     145                 :            : 
     146                 :         16 :     virtual void handle_poll(vector<pollfd>& fds)
     147                 :            :       {
     148         [ +  + ]:         35 :         for (unsigned i=0; i < fds.size(); ++i)
     149 [ +  + ][ +  - ]:         19 :           if (fds[i].fd == fdin || fds[i].fd == fdout)
                 [ +  - ]
     150                 :            :             {
     151                 :         19 :               bool err = false;
     152                 :            : 
     153                 :            :               // need to send a signal?
     154 [ +  + ][ +  - ]:         19 :               if (fds[i].revents & POLLOUT && IN &&
         [ +  - ][ +  + ]
     155                 :            :                   interrupts_sent < pending_interrupts)
     156                 :            :                 {
     157 [ +  - ][ +  - ]:          3 :                   if (send_command("quit\n") == 0)
         [ +  - ][ +  - ]
     158                 :          3 :                     ++interrupts_sent;
     159                 :            :                   else
     160                 :          0 :                     err = true;
     161                 :            :                 }
     162                 :            : 
     163                 :            :               // have data to read?
     164 [ +  + ][ +  - ]:         19 :               if (fds[i].revents & POLLIN && OUT)
                 [ +  + ]
     165                 :            :                 {
     166                 :            :                   char buf[4096];
     167 [ +  - ][ -  + ]:          7 :                   if (!prefix.empty())
     168                 :            :                     {
     169                 :            :                       // If we have a line prefix, then read lines one at a
     170                 :            :                       // time and copy out with the prefix.
     171                 :          0 :                       errno = 0;
     172 [ #  # ][ #  # ]:          0 :                       while (fgets(buf, sizeof(buf), OUT))
     173 [ #  # ][ #  # ]:          0 :                         cout << prefix << buf;
     174         [ #  # ]:          0 :                       if (errno != EAGAIN)
     175                 :          0 :                         err = true;
     176                 :            :                     }
     177                 :            :                   else
     178                 :            :                     {
     179                 :            :                       // Otherwise read an entire block of data at once.
     180         [ +  - ]:          7 :                       size_t rc = fread(buf, 1, sizeof(buf), OUT);
     181         [ +  - ]:          7 :                       if (rc > 0)
     182                 :            :                         {
     183                 :            :                           // NB: The buf could contain binary data,
     184                 :            :                           // including \0, so write as a block instead of
     185                 :            :                           // the usual <<string.
     186         [ +  - ]:          7 :                           cout.write(buf, rc);
     187                 :            :                         }
     188                 :            :                       else
     189                 :          7 :                         err = true;
     190                 :            :                     }
     191                 :            :                 }
     192                 :            : 
     193                 :            :               // any errors?
     194 [ +  - ][ +  + ]:         19 :               if (err || fds[i].revents & ~(POLLIN|POLLOUT))
                 [ +  + ]
     195                 :          3 :                 close();
     196                 :            :             }
     197                 :         16 :       }
     198                 :            : 
     199                 :         10 :     string get_reply()
     200                 :            :       {
     201                 :            :         // Some schemes like unix may have stdout and stderr mushed together.
     202                 :            :         // There shouldn't be anything except dbug messages on stderr before we
     203                 :            :         // actually start running, and there's no get_reply after that.  So
     204                 :            :         // we'll just loop and skip those that start with "stapsh:".
     205                 :            :         char reply[4096];
     206 [ +  - ][ +  - ]:         10 :         while (fgets(reply, sizeof(reply), OUT))
     207                 :            :           {
     208 [ +  - ][ +  - ]:         10 :             if (!startswith(reply, "stapsh:"))
         [ +  - ][ +  - ]
     209         [ +  - ]:         10 :               return reply;
     210                 :            : 
     211                 :            :             // Why not clog here?  Well, once things get running we won't be
     212                 :            :             // able to distinguish stdout/err, so trying to fake it here would
     213                 :            :             // be less consistent than just keeping it merged.
     214         [ #  # ]:          0 :             cout << reply;
     215                 :            :           }
     216                 :            : 
     217                 :            :         // Reached EOF, nothing to reply...
     218         [ #  # ]:         10 :         return "";
     219                 :            :       }
     220                 :            : 
     221                 :         13 :     int send_command(const string& cmd)
     222                 :            :       {
     223         [ -  + ]:         13 :         if (!IN)
     224                 :          0 :           return 2;
     225   [ +  -  -  + ]:         26 :         if (fputs(cmd.c_str(), IN) < 0 ||
                 [ -  + ]
     226                 :         13 :             fflush(IN) != 0)
     227                 :          0 :           return 1;
     228                 :         13 :         return 0;
     229                 :            :       }
     230                 :            : 
     231                 :          3 :     int send_file(const string& filename, const string& dest)
     232                 :            :       {
     233                 :          3 :         int rc = 0;
     234 [ +  - ][ +  - ]:          3 :         FILE* f = fopen(filename.c_str(), "r");
     235         [ -  + ]:          3 :         if (!f)
     236                 :          0 :           return 1;
     237                 :            : 
     238                 :            :         struct stat fs;
     239                 :          3 :         rc = fstat(fileno(f), &fs);
     240         [ +  - ]:          3 :         if (!rc)
     241                 :            :           {
     242         [ +  - ]:          3 :             ostringstream cmd;
     243 [ +  - ][ +  - ]:          3 :             cmd << "file " << fs.st_size << " " << dest << "\n";
         [ +  - ][ +  - ]
                 [ +  - ]
     244 [ +  - ][ +  - ]:          3 :             rc = send_command(cmd.str());
         [ +  - ][ +  - ]
     245                 :            :           }
     246                 :            : 
     247                 :          3 :         off_t i = 0;
     248 [ +  - ][ +  + ]:        216 :         while (!rc && i < fs.st_size)
                 [ +  + ]
     249                 :            :           {
     250                 :            :             char buf[4096];
     251                 :        213 :             size_t r = sizeof(buf);
     252         [ +  + ]:        213 :             if (fs.st_size - i < (off_t)r)
     253                 :          3 :               r = fs.st_size - i;
     254         [ +  - ]:        213 :             r = fread(buf, 1, r, f);
     255         [ -  + ]:        213 :             if (r == 0)
     256                 :          0 :               rc = 1;
     257                 :            :             else
     258                 :            :               {
     259         [ +  - ]:        213 :                 size_t w = fwrite(buf, 1, r, IN);
     260         [ -  + ]:        213 :                 if (w != r)
     261                 :          0 :                   rc = 1;
     262                 :            :                 else
     263                 :        213 :                   i += w;
     264                 :            :               }
     265                 :            :           }
     266         [ +  - ]:          3 :         if (!rc)
     267         [ +  - ]:          3 :           rc = fflush(IN);
     268                 :            : 
     269         [ +  - ]:          3 :         fclose(f);
     270                 :            : 
     271         [ +  - ]:          3 :         if (!rc)
     272                 :            :           {
     273         [ +  - ]:          3 :             string reply = get_reply();
     274 [ +  - ][ -  + ]:          3 :             if (reply != "OK\n")
     275                 :            :               {
     276                 :          0 :                 rc = 1;
     277         [ #  # ]:          0 :                 if (s->verbose > 0)
     278                 :            :                   {
     279 [ #  # ][ #  # ]:          0 :                     if (reply.empty())
     280 [ #  # ][ #  # ]:          0 :                       clog << _("stapsh file ERROR: no reply") << endl;
     281                 :            :                     else
     282 [ #  # ][ #  # ]:          0 :                       clog << _F("stapsh file replied %s", reply.c_str());
         [ #  # ][ #  # ]
     283                 :            :                   }
     284         [ +  - ]:          3 :               }
     285                 :            :           }
     286                 :            : 
     287                 :          3 :         return rc;
     288                 :            :       }
     289                 :            : 
     290                 :         12 :     static string qpencode(const string& str)
     291                 :            :       {
     292         [ +  - ]:         12 :         ostringstream o;
     293 [ +  - ][ +  - ]:         12 :         o << setfill('0') << hex;
     294 [ +  - ][ +  + ]:        192 :         for (const char* s = str.c_str(); *s; ++s)
     295 [ +  - ][ +  - ]:        180 :           if (*s >= 33 && *s <= 126 && *s != 61)
                 [ +  - ]
     296         [ +  - ]:        180 :             o << *s;
     297                 :            :           else
     298 [ #  # ][ #  # ]:          0 :             o << '=' << setw(2) << (unsigned)(unsigned char) *s;
                 [ #  # ]
     299 [ +  - ][ +  - ]:         12 :         return o.str();
     300                 :            :       }
     301                 :            : 
     302                 :            :   protected:
     303                 :          5 :     stapsh(systemtap_session& s)
     304                 :            :       : remote(s), interrupts_sent(0),
     305         [ +  - ]:          5 :         fdin(-1), fdout(-1), IN(0), OUT(0)
     306                 :          5 :       {}
     307                 :            : 
     308                 :          3 :     virtual int prepare()
     309                 :            :       {
     310                 :          3 :         int rc = 0;
     311                 :            : 
     312 [ +  - ][ +  - ]:          3 :         string localmodule = s->tmpdir + "/" + s->module_name + ".ko";
         [ +  - ][ +  - ]
                 [ +  - ]
     313         [ +  - ]:          3 :         string remotemodule = s->module_name + ".ko";
     314 [ +  - ][ -  + ]:          3 :         if ((rc = send_file(localmodule, remotemodule)))
     315                 :          0 :           return rc;
     316                 :            : 
     317 [ +  - ][ +  - ]:          6 :         if (file_exists(localmodule + ".sgn") &&
         [ -  + ][ #  # ]
         [ #  # ][ +  - ]
                 [ +  - ]
           [ -  +  #  # ]
     318 [ #  # ][ #  # ]:          3 :             (rc = send_file(localmodule + ".sgn", remotemodule + ".sgn")))
         [ -  + ][ #  # ]
         [ -  + ][ #  # ]
           [ #  #  #  # ]
     319                 :          0 :           return rc;
     320                 :            : 
     321 [ +  - ][ -  + ]:          3 :         if (!s->uprobes_path.empty())
     322                 :            :           {
     323 [ #  # ][ #  # ]:          0 :             string remoteuprobes = basename(s->uprobes_path.c_str());
     324 [ #  # ][ #  # ]:          0 :             if ((rc = send_file(s->uprobes_path, remoteuprobes)))
     325                 :          0 :               return rc;
     326                 :            : 
     327 [ #  # ][ #  # ]:          0 :             if (file_exists(s->uprobes_path + ".sgn") &&
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
           [ #  #  #  # ]
     328 [ #  # ][ #  # ]:          0 :                 (rc = send_file(s->uprobes_path + ".sgn", remoteuprobes + ".sgn")))
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
           [ #  #  #  # ]
     329 [ #  # ][ #  # ]:          0 :               return rc;
     330                 :            :           }
     331                 :            : 
     332 [ +  - ][ +  - ]:          3 :         return rc;
     333                 :            :       }
     334                 :            : 
     335                 :          3 :     virtual int start()
     336                 :            :       {
     337                 :            :         // Send the staprun args
     338                 :            :         // NB: The remote is left to decide its own staprun path
     339 [ +  - ][ +  - ]:          3 :         ostringstream run("run", ios::out | ios::ate);
                 [ +  - ]
     340 [ +  - ][ +  - ]:          3 :         vector<string> cmd = make_run_command(*s, ".", remote_version);
                 [ +  - ]
     341                 :            : 
     342                 :            :         // PR13354: identify our remote index/url
     343 [ +  - ][ +  - ]:          6 :         if (strverscmp("1.7", remote_version.c_str()) <= 0 && // -r supported?
         [ +  - ][ +  - ]
     344         [ +  - ]:          3 :             ! staprun_r_arg.empty())
     345                 :            :           {
     346 [ +  - ][ +  - ]:          3 :             cmd.push_back ("-r");
                 [ +  - ]
     347         [ +  - ]:          3 :             cmd.push_back (staprun_r_arg);
     348                 :            :           }
     349                 :            : 
     350         [ +  + ]:         15 :         for (unsigned i = 1; i < cmd.size(); ++i)
     351 [ +  - ][ +  - ]:         12 :           run << ' ' << qpencode(cmd[i]);
         [ +  - ][ +  - ]
     352         [ +  - ]:          3 :         run << '\n';
     353                 :            : 
     354 [ +  - ][ +  - ]:          3 :         int rc = send_command(run.str());
                 [ +  - ]
     355                 :            : 
     356         [ +  - ]:          3 :         if (!rc)
     357                 :            :           {
     358         [ +  - ]:          3 :             string reply = get_reply();
     359 [ +  - ][ -  + ]:          3 :             if (reply != "OK\n")
     360                 :            :               {
     361                 :          0 :                 rc = 1;
     362         [ #  # ]:          0 :                 if (s->verbose > 0)
     363                 :            :                   {
     364 [ #  # ][ #  # ]:          0 :                     if (reply.empty())
     365 [ #  # ][ #  # ]:          0 :                       clog << _("stapsh run ERROR: no reply") << endl;
     366                 :            :                     else
     367 [ #  # ][ #  # ]:          0 :                       clog << _F("stapsh run replied %s", reply.c_str());
         [ #  # ][ #  # ]
     368                 :            :                   }
     369         [ +  - ]:          3 :               }
     370                 :            :           }
     371                 :            : 
     372         [ +  - ]:          3 :         if (!rc)
     373                 :            :           {
     374         [ +  - ]:          3 :             long flags = fcntl(fdout, F_GETFL) | O_NONBLOCK;
     375         [ +  - ]:          3 :             fcntl(fdout, F_SETFL, flags);
     376                 :            :           }
     377                 :            :         else
     378                 :            :           // If run failed for any reason, then this
     379                 :            :           // connection is effectively dead to us.
     380         [ #  # ]:          0 :           close();
     381                 :            : 
     382 [ +  - ][ +  - ]:          3 :         return rc;
     383                 :            :       }
     384                 :            : 
     385                 :         15 :     void close()
     386                 :            :       {
     387         [ +  + ]:         15 :         if (IN) fclose(IN);
     388         [ +  + ]:         15 :         if (OUT) fclose(OUT);
     389                 :         15 :         IN = OUT = NULL;
     390                 :         15 :         fdin = fdout = -1;
     391                 :         15 :       }
     392                 :            : 
     393                 :          7 :     virtual int finish()
     394                 :            :       {
     395                 :          7 :         close();
     396                 :          7 :         return 0;
     397                 :            :       }
     398                 :            : 
     399                 :          4 :     void set_child_fds(int in, int out)
     400                 :            :       {
     401 [ +  - ][ +  - ]:          4 :         if (fdin >= 0 || fdout >= 0 || IN || OUT)
         [ +  - ][ -  + ]
     402 [ #  # ][ #  # ]:          0 :           throw runtime_error(_("stapsh file descriptors already set"));
     403                 :            : 
     404                 :          4 :         fdin = in;
     405                 :          4 :         fdout = out;
     406                 :          4 :         IN = fdopen(fdin, "w");
     407                 :          4 :         OUT = fdopen(fdout, "r");
     408 [ +  - ][ -  + ]:          4 :         if (!IN || !OUT)
     409 [ #  # ][ #  # ]:          0 :           throw runtime_error(_("invalid file descriptors for stapsh"));
     410                 :            : 
     411 [ +  - ][ +  - ]:          4 :         if (send_command("stap " VERSION "\n"))
         [ +  - ][ -  + ]
     412 [ #  # ][ #  # ]:          0 :           throw runtime_error(_("error sending hello to stapsh"));
     413                 :            : 
     414         [ +  - ]:          4 :         string reply = get_reply();
     415 [ +  - ][ -  + ]:          4 :         if (reply.empty())
     416 [ #  # ][ #  # ]:          0 :           throw runtime_error(_("error receiving hello from stapsh"));
     417                 :            : 
     418                 :            :         // stapsh VERSION MACHINE RELEASE
     419         [ +  - ]:          4 :         vector<string> uname;
     420 [ +  - ][ +  - ]:          4 :         tokenize(reply, uname, " \t\r\n");
                 [ +  - ]
     421 [ +  - ][ +  - ]:          4 :         if (uname.size() != 4 || uname[0] != "stapsh")
         [ -  + ][ -  + ]
     422 [ #  # ][ #  # ]:          0 :           throw runtime_error(_("failed to get uname from stapsh"));
     423                 :            : 
     424                 :            :         // We assume that later versions will know how to talk to us.
     425                 :            :         // Looking backward, we use this for make_run_command().
     426         [ +  - ]:          4 :         this->remote_version = uname[1];
     427                 :            : 
     428 [ +  - ][ +  - ]:          4 :         this->s = s->clone(uname[2], uname[3]);
                 [ +  - ]
     429                 :          4 :       }
     430                 :            : 
     431                 :            :   public:
     432 [ +  - ][ +  - ]:          5 :     virtual ~stapsh() { close(); }
                 [ -  + ]
     433                 :            : };
     434                 :            : 
     435                 :            : 
     436                 :            : // direct_stapsh is meant only for testing, as a way to exercise the stapsh
     437                 :            : // mechanisms without requiring test machines to have actual remote access.
     438                 :            : class direct_stapsh : public stapsh {
     439                 :            :   private:
     440                 :            :     pid_t child;
     441                 :            : 
     442                 :          4 :     direct_stapsh(systemtap_session& s)
     443                 :          4 :       : stapsh(s), child(0)
     444                 :            :       {
     445                 :            :         int in, out;
     446         [ +  - ]:          4 :         vector<string> cmd;
     447 [ +  - ][ +  - ]:          4 :         cmd.push_back(BINDIR "/stapsh");
                 [ +  - ]
     448         [ -  + ]:          4 :         if (s.perpass_verbose[4] > 1)
     449 [ #  # ][ #  # ]:          0 :           cmd.push_back("-v");
                 [ #  # ]
     450         [ -  + ]:          4 :         if (s.perpass_verbose[4] > 2)
     451 [ #  # ][ #  # ]:          0 :           cmd.push_back("-v");
                 [ #  # ]
     452                 :            : 
     453                 :            :         // mask signals while we spawn, so we can simulate manual signals to
     454                 :            :         // the "remote" target, as we must for the real ssh_remote case.
     455                 :            :         {
     456                 :          4 :           stap_sigmasker masked;
     457         [ +  - ]:          4 :           child = stap_spawn_piped(s.verbose, cmd, &in, &out);
     458                 :            :         }
     459                 :            : 
     460         [ -  + ]:          4 :         if (child <= 0)
     461 [ #  # ][ #  # ]:          0 :           throw runtime_error(_("error launching stapsh"));
     462                 :            : 
     463                 :            :         try
     464                 :            :           {
     465         [ +  - ]:          4 :             set_child_fds(in, out);
     466                 :            :           }
     467         [ #  # ]:            :         catch (runtime_error&)
     468                 :            :           {
     469         [ #  # ]:            :             finish();
     470                 :            :             throw;
     471         [ +  - ]:          4 :           }
     472                 :          4 :       }
     473                 :            : 
     474                 :          7 :     virtual int finish()
     475                 :            :       {
     476                 :          7 :         int rc = stapsh::finish();
     477         [ +  + ]:          7 :         if (child <= 0)
     478                 :          3 :           return rc;
     479                 :            : 
     480                 :          4 :         int rc2 = stap_waitpid(s->verbose, child);
     481                 :          4 :         child = 0;
     482         [ -  + ]:          7 :         return rc ?: rc2;
     483                 :            :       }
     484                 :            : 
     485                 :            :   public:
     486                 :            :     friend class remote;
     487                 :            : 
     488 [ +  - ][ -  + ]:          8 :     virtual ~direct_stapsh() { finish(); }
     489                 :            : };
     490                 :            : 
     491                 :            : 
     492                 :            : // Connect to an existing stapsh on a unix socket.
     493                 :            : class unix_stapsh : public stapsh {
     494                 :            :   private:
     495                 :            : 
     496                 :          1 :     unix_stapsh(systemtap_session& s, const uri_decoder& ud)
     497                 :          1 :       : stapsh(s)
     498                 :            :       {
     499                 :            :         sockaddr_un server;
     500                 :          1 :         server.sun_family = AF_UNIX;
     501 [ +  - ][ -  + ]:          1 :         if (ud.path.empty())
     502 [ #  # ][ #  # ]:          0 :           throw runtime_error(_("unix target requires a /path"));
     503 [ +  - ][ -  + ]:          1 :         if (ud.path.size() > sizeof(server.sun_path) - 1)
     504 [ #  # ][ #  # ]:          0 :           throw runtime_error(_("unix target /path is too long"));
     505         [ +  - ]:          1 :         strcpy(server.sun_path, ud.path.c_str());
     506                 :            : 
     507         [ -  + ]:          1 :         if (ud.has_authority)
     508 [ #  # ][ #  # ]:          0 :           throw runtime_error(_("unix target doesn't support a hostname"));
     509         [ -  + ]:          1 :         if (ud.has_query)
     510 [ #  # ][ #  # ]:          0 :           throw runtime_error(_("unix target URI doesn't support a ?query"));
     511         [ -  + ]:          1 :         if (ud.has_fragment)
     512 [ #  # ][ #  # ]:          0 :           throw runtime_error(_("unix target URI doesn't support a #fragment"));
     513                 :            : 
     514                 :          1 :         int fd = socket(AF_UNIX, SOCK_STREAM, 0);
     515         [ -  + ]:          1 :         if (fd <= 0)
     516 [ #  # ][ #  # ]:          0 :           throw runtime_error(_("error opening a socket"));
     517                 :            : 
     518 [ +  - ][ +  - ]:          1 :         if (connect(fd, (struct sockaddr *)&server, SUN_LEN(&server)) < 0)
     519                 :            :           {
     520                 :          1 :             const char *msg = strerror(errno);
     521         [ +  - ]:          1 :             ::close(fd);
     522 [ +  - ][ +  - ]:          1 :             throw runtime_error(_F("error connecting to socket: %s", msg));
     523                 :            :           }
     524                 :            : 
     525                 :            :         // Try to dup it, so class stapsh can have truly separate fds for its
     526                 :            :         // fdopen handles.  If it fails for some reason, it can still work with
     527                 :            :         // just one though.
     528                 :          0 :         int fd2 = dup(fd);
     529         [ #  # ]:          0 :         if (fd2 < 0)
     530                 :          0 :           fd2 = fd;
     531                 :            : 
     532                 :            :         try
     533                 :            :           {
     534         [ #  # ]:          0 :             set_child_fds(fd, fd2);
     535                 :            :           }
     536         [ #  # ]:            :         catch (runtime_error&)
     537                 :            :           {
     538         [ #  # ]:            :             finish();
     539         [ #  # ]:            :             ::close(fd);
     540         [ #  # ]:            :             ::close(fd2);
     541                 :          1 :             throw;
     542                 :            :           }
     543                 :          1 :       }
     544                 :            : 
     545                 :            :   public:
     546                 :            :     friend class remote;
     547                 :            : 
     548 [ #  # ][ #  # ]:          0 :     virtual ~unix_stapsh() { finish(); }
     549                 :            : };
     550                 :            : 
     551                 :            : 
     552                 :            : // stapsh-based ssh_remote
     553                 :            : class ssh_remote : public stapsh {
     554                 :            :   private:
     555                 :            :     pid_t child;
     556                 :            : 
     557                 :          0 :     ssh_remote(systemtap_session& s): stapsh(s), child(0) {}
     558                 :            : 
     559                 :          0 :     int connect(const string& host, const string& port)
     560                 :            :       {
     561                 :          0 :         int rc = 0;
     562                 :            :         int in, out;
     563         [ #  # ]:          0 :         vector<string> cmd;
     564 [ #  # ][ #  # ]:          0 :         cmd.push_back("ssh");
                 [ #  # ]
     565 [ #  # ][ #  # ]:          0 :         cmd.push_back("-q");
                 [ #  # ]
     566         [ #  # ]:          0 :         cmd.push_back(host);
     567 [ #  # ][ #  # ]:          0 :         if (!port.empty())
     568                 :            :           {
     569 [ #  # ][ #  # ]:          0 :             cmd.push_back("-p");
                 [ #  # ]
     570         [ #  # ]:          0 :             cmd.push_back(port);
     571                 :            :           }
     572                 :            : 
     573                 :            :         // This is crafted so that we get a silent failure with status 127 if
     574                 :            :         // the command is not found.  The combination of -P and $cmd ensures
     575                 :            :         // that we pull the command out of the PATH, not aliases or such.
     576         [ #  # ]:          0 :         string stapsh_cmd = "cmd=`type -P stapsh || exit 127` && exec \"$cmd\"";
     577         [ #  # ]:          0 :         if (s->perpass_verbose[4] > 1)
     578         [ #  # ]:          0 :           stapsh_cmd.append(" -v");
     579         [ #  # ]:          0 :         if (s->perpass_verbose[4] > 2)
     580         [ #  # ]:          0 :           stapsh_cmd.append(" -v");
     581                 :            :         // NB: We need to explicitly choose bash, as $SHELL may be weird...
     582 [ #  # ][ #  # ]:          0 :         cmd.push_back("/bin/bash -c '" + stapsh_cmd + "'");
         [ #  # ][ #  # ]
                 [ #  # ]
     583                 :            : 
     584                 :            :         // mask signals while we spawn, so we can manually send even tty
     585                 :            :         // signals *through* ssh rather than to ssh itself
     586                 :            :         {
     587                 :          0 :           stap_sigmasker masked;
     588         [ #  # ]:          0 :           child = stap_spawn_piped(s->verbose, cmd, &in, &out);
     589                 :            :         }
     590                 :            : 
     591         [ #  # ]:          0 :         if (child <= 0)
     592 [ #  # ][ #  # ]:          0 :           throw runtime_error(_("error launching stapsh"));
     593                 :            : 
     594                 :            :         try
     595                 :            :           {
     596         [ #  # ]:          0 :             set_child_fds(in, out);
     597                 :            :           }
     598         [ #  # ]:            :         catch (runtime_error&)
     599                 :            :           {
     600         [ #  # ]:            :             rc = finish();
     601                 :            : 
     602                 :            :             // ssh itself signals errors with 255
     603         [ #  # ]:            :             if (rc == 255)
     604   [ #  #  #  # ]:            :               throw runtime_error(_("error establishing ssh connection"));
     605                 :            : 
     606                 :            :             // If rc == 127, that's command-not-found, so we let ::create()
     607                 :            :             // try again in legacy mode.  But only do this if there's a single
     608                 :            :             // remote, as the old code didn't handle ttys well with multiple
     609                 :            :             // remotes.  Otherwise, throw up again. *barf*
     610   [ #  #  #  #  :            :             if (rc != 127 || s->remote_uris.size() > 1)
                   #  # ]
     611                 :            :               throw;
     612                 :            :           }
     613                 :            : 
     614 [ #  # ][ #  # ]:          0 :         return rc;
     615                 :            :       }
     616                 :            : 
     617                 :          0 :     int finish()
     618                 :            :       {
     619                 :          0 :         int rc = stapsh::finish();
     620         [ #  # ]:          0 :         if (child <= 0)
     621                 :          0 :           return rc;
     622                 :            : 
     623                 :          0 :         int rc2 = stap_waitpid(s->verbose, child);
     624                 :          0 :         child = 0;
     625         [ #  # ]:          0 :         return rc ?: rc2;
     626                 :            :       }
     627                 :            : 
     628                 :            :   public:
     629                 :            : 
     630                 :            :     static remote* create(systemtap_session& s, const string& host);
     631                 :            :     static remote* create(systemtap_session& s, const uri_decoder& ud);
     632                 :            : 
     633 [ #  # ][ #  # ]:          0 :     virtual ~ssh_remote() { finish(); }
     634                 :            : };
     635                 :            : 
     636                 :            : 
     637                 :            : // ssh connection without stapsh, for legacy stap installations
     638                 :            : // NB: ssh commands use a tty (-t) so signals are passed along to the remote.
     639                 :            : // It does this by putting the local tty in raw mode, so it only works for tty
     640                 :            : // signals, and only for a single remote at a time.
     641                 :            : class ssh_legacy_remote : public remote {
     642                 :            :   private:
     643                 :            :     vector<string> ssh_args, scp_args;
     644                 :            :     string ssh_control;
     645                 :            :     string host, port, tmpdir;
     646                 :            :     pid_t child;
     647                 :            : 
     648                 :          0 :     ssh_legacy_remote(systemtap_session& s, const string& host, const string& port)
     649 [ #  # ][ #  # ]:          0 :       : remote(s), host(host), port(port), child(0)
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
     650                 :            :       {
     651         [ #  # ]:          0 :         open_control_master();
     652                 :            :         try
     653                 :            :           {
     654         [ #  # ]:          0 :             get_uname();
     655                 :            :           }
     656         [ #  # ]:            :         catch (runtime_error&)
     657                 :            :           {
     658         [ #  # ]:            :             close_control_master();
     659                 :            :             throw;
     660                 :            :           }
     661                 :          0 :       }
     662                 :            : 
     663                 :          0 :     void open_control_master()
     664                 :            :       {
     665                 :            :         static unsigned index = 0;
     666                 :            : 
     667 [ #  # ][ #  # ]:          0 :         if (s->tmpdir.empty()) // sanity check, shouldn't happen
     668 [ #  # ][ #  # ]:          0 :           throw runtime_error(_("No tmpdir available for ssh control master"));
     669                 :            : 
     670 [ #  # ][ #  # ]:          0 :         ssh_control = s->tmpdir + "/ssh_remote_control_" + lex_cast(++index);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     671                 :            : 
     672         [ #  # ]:          0 :         scp_args.clear();
     673 [ #  # ][ #  # ]:          0 :         scp_args.push_back("scp");
                 [ #  # ]
     674 [ #  # ][ #  # ]:          0 :         scp_args.push_back("-q");
                 [ #  # ]
     675 [ #  # ][ #  # ]:          0 :         scp_args.push_back("-o");
                 [ #  # ]
     676 [ #  # ][ #  # ]:          0 :         scp_args.push_back("ControlPath=" + ssh_control);
                 [ #  # ]
     677                 :            : 
     678         [ #  # ]:          0 :         ssh_args = scp_args;
     679         [ #  # ]:          0 :         ssh_args[0] = "ssh";
     680         [ #  # ]:          0 :         ssh_args.push_back(host);
     681                 :            : 
     682 [ #  # ][ #  # ]:          0 :         if (!port.empty())
     683                 :            :           {
     684 [ #  # ][ #  # ]:          0 :             scp_args.push_back("-P");
                 [ #  # ]
     685         [ #  # ]:          0 :             scp_args.push_back(port);
     686 [ #  # ][ #  # ]:          0 :             ssh_args.push_back("-p");
                 [ #  # ]
     687         [ #  # ]:          0 :             ssh_args.push_back(port);
     688                 :            :           }
     689                 :            : 
     690                 :            :         // NB: ssh -f will stay in the foreground until authentication is
     691                 :            :         // complete and the control socket is created, so we know it's ready to
     692                 :            :         // go when stap_system returns.
     693         [ #  # ]:          0 :         vector<string> cmd = ssh_args;
     694 [ #  # ][ #  # ]:          0 :         cmd.push_back("-f");
                 [ #  # ]
     695 [ #  # ][ #  # ]:          0 :         cmd.push_back("-N");
                 [ #  # ]
     696 [ #  # ][ #  # ]:          0 :         cmd.push_back("-M");
                 [ #  # ]
     697         [ #  # ]:          0 :         int rc = stap_system(s->verbose, cmd);
     698         [ #  # ]:          0 :         if (rc != 0)
     699                 :          0 :           throw runtime_error(_F("failed to create an ssh control master for %s : rc= %d",
     700 [ #  # ][ #  # ]:          0 :                                  host.c_str(), rc));
                 [ #  # ]
     701                 :            : 
     702         [ #  # ]:          0 :         if (s->verbose>1)
     703 [ #  # ][ #  # ]:          0 :           clog << _F("Created ssh control master at %s",
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
     704 [ #  # ][ #  # ]:          0 :                      lex_cast_qstring(ssh_control).c_str()) << endl;
     705                 :          0 :       }
     706                 :            : 
     707                 :          0 :     void close_control_master()
     708                 :            :       {
     709 [ #  # ][ #  # ]:          0 :         if (ssh_control.empty())
     710                 :          0 :           return;
     711                 :            : 
     712         [ #  # ]:          0 :         vector<string> cmd = ssh_args;
     713 [ #  # ][ #  # ]:          0 :         cmd.push_back("-O");
                 [ #  # ]
     714 [ #  # ][ #  # ]:          0 :         cmd.push_back("exit");
                 [ #  # ]
     715         [ #  # ]:          0 :         int rc = stap_system(s->verbose, cmd, true, true);
     716         [ #  # ]:          0 :         if (rc != 0)
     717 [ #  # ][ #  # ]:          0 :           cerr << _F("failed to stop the ssh control master for %s : rc=%d",
         [ #  # ][ #  # ]
     718         [ #  # ]:          0 :                      host.c_str(), rc) << endl;
     719                 :            : 
     720         [ #  # ]:          0 :         ssh_control.clear();
     721         [ #  # ]:          0 :         scp_args.clear();
     722 [ #  # ][ #  # ]:          0 :         ssh_args.clear();
     723                 :            :       }
     724                 :            : 
     725                 :          0 :     void get_uname()
     726                 :            :       {
     727         [ #  # ]:          0 :         ostringstream out;
     728         [ #  # ]:          0 :         vector<string> uname;
     729         [ #  # ]:          0 :         vector<string> cmd = ssh_args;
     730 [ #  # ][ #  # ]:          0 :         cmd.push_back("-t");
                 [ #  # ]
     731 [ #  # ][ #  # ]:          0 :         cmd.push_back("uname -rm");
                 [ #  # ]
     732         [ #  # ]:          0 :         int rc = stap_system_read(s->verbose, cmd, out);
     733         [ #  # ]:          0 :         if (rc == 0)
     734 [ #  # ][ #  # ]:          0 :           tokenize(out.str(), uname, " \t\r\n");
         [ #  # ][ #  # ]
                 [ #  # ]
     735         [ #  # ]:          0 :         if (uname.size() != 2)
     736 [ #  # ][ #  # ]:          0 :           throw runtime_error(_F("failed to get uname from %s : rc= %d", host.c_str(), rc));
                 [ #  # ]
     737                 :          0 :         const string& release = uname[0];
     738                 :          0 :         const string& arch = uname[1];
     739                 :            :         // XXX need to deal with command-line vs. implied arch/release
     740 [ #  # ][ #  # ]:          0 :         this->s = s->clone(arch, release);
         [ #  # ][ #  # ]
     741                 :          0 :       }
     742                 :            : 
     743                 :          0 :     int start()
     744                 :            :       {
     745                 :            :         int rc;
     746 [ #  # ][ #  # ]:          0 :         string localmodule = s->tmpdir + "/" + s->module_name + ".ko";
         [ #  # ][ #  # ]
                 [ #  # ]
     747         [ #  # ]:          0 :         string tmpmodule;
     748                 :            : 
     749                 :            :         // Make a remote tempdir.
     750                 :            :         {
     751         [ #  # ]:          0 :           ostringstream out;
     752         [ #  # ]:          0 :           vector<string> vout;
     753         [ #  # ]:          0 :           vector<string> cmd = ssh_args;
     754 [ #  # ][ #  # ]:          0 :           cmd.push_back("-t");
                 [ #  # ]
     755 [ #  # ][ #  # ]:          0 :           cmd.push_back("mktemp -d -t stapXXXXXX");
                 [ #  # ]
     756         [ #  # ]:          0 :           rc = stap_system_read(s->verbose, cmd, out);
     757         [ #  # ]:          0 :           if (rc == 0)
     758 [ #  # ][ #  # ]:          0 :             tokenize(out.str(), vout, "\r\n");
         [ #  # ][ #  # ]
                 [ #  # ]
     759         [ #  # ]:          0 :           if (vout.size() != 1)
     760                 :            :             {
     761 [ #  # ][ #  # ]:          0 :               cerr << _F("failed to make a tempdir on %s : rc=%d",
         [ #  # ][ #  # ]
     762         [ #  # ]:          0 :                          host.c_str(), rc) << endl;
     763                 :          0 :               return -1;
     764                 :            :             }
     765         [ #  # ]:          0 :           tmpdir = vout[0];
     766 [ #  # ][ #  # ]:          0 :           tmpmodule = tmpdir + "/" + s->module_name + ".ko";
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     767                 :            :         }
     768                 :            : 
     769                 :            :         // Transfer the module.
     770         [ #  # ]:          0 :         if (rc == 0)
     771                 :            :           {
     772         [ #  # ]:          0 :             vector<string> cmd = scp_args;
     773         [ #  # ]:          0 :             cmd.push_back(localmodule);
     774 [ #  # ][ #  # ]:          0 :             cmd.push_back(host + ":" + tmpmodule);
         [ #  # ][ #  # ]
                 [ #  # ]
     775         [ #  # ]:          0 :             rc = stap_system(s->verbose, cmd);
     776         [ #  # ]:          0 :             if (rc != 0)
     777 [ #  # ][ #  # ]:          0 :               cerr << _F("failed to copy the module to %s : rc=%d",
         [ #  # ][ #  # ]
     778 [ #  # ][ #  # ]:          0 :                          host.c_str(), rc) << endl;
     779                 :            :           }
     780                 :            : 
     781                 :            :         // Transfer the module signature.
     782 [ #  # ][ #  # ]:          0 :         if (rc == 0 && file_exists(localmodule + ".sgn"))
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
           [ #  #  #  # ]
     783                 :            :           {
     784         [ #  # ]:          0 :             vector<string> cmd = scp_args;
     785 [ #  # ][ #  # ]:          0 :             cmd.push_back(localmodule + ".sgn");
                 [ #  # ]
     786 [ #  # ][ #  # ]:          0 :             cmd.push_back(host + ":" + tmpmodule + ".sgn");
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     787         [ #  # ]:          0 :             rc = stap_system(s->verbose, cmd);
     788         [ #  # ]:          0 :             if (rc != 0)
     789 [ #  # ][ #  # ]:          0 :               cerr << _F("failed to copy the module signature to %s : rc=%d",
         [ #  # ][ #  # ]
     790 [ #  # ][ #  # ]:          0 :                          host.c_str(), rc) << endl;
     791                 :            :           }
     792                 :            : 
     793                 :            :         // What about transfering uprobes.ko?  In this ssh "legacy" mode, we
     794                 :            :         // don't the remote systemtap version, but -uPATH wasn't added until
     795                 :            :         // 1.4.  Rather than risking a getopt error, we'll just assume that
     796                 :            :         // this isn't supported.  The remote will just have to provide its own
     797                 :            :         // uprobes.ko in SYSTEMTAP_RUNTIME or already loaded.
     798                 :            : 
     799                 :            :         // Run the module on the remote.
     800         [ #  # ]:          0 :         if (rc == 0) {
     801         [ #  # ]:          0 :           vector<string> cmd = ssh_args;
     802 [ #  # ][ #  # ]:          0 :           cmd.push_back("-t");
                 [ #  # ]
     803                 :            :           // We don't know the actual version, but all <=1.3 are approx equal.
     804 [ #  # ][ #  # ]:          0 :           vector<string> staprun_cmd = make_run_command(*s, tmpdir, "1.3");
                 [ #  # ]
     805         [ #  # ]:          0 :           staprun_cmd[0] = "staprun"; // NB: The remote decides its own path
     806                 :            :           // NB: PR13354: we assume legacy installations don't have
     807                 :            :           // staprun -r support, so we ignore staprun_r_arg.
     808 [ #  # ][ #  # ]:          0 :           cmd.push_back(cmdstr_join(staprun_cmd));
                 [ #  # ]
     809         [ #  # ]:          0 :           pid_t pid = stap_spawn(s->verbose, cmd);
     810         [ #  # ]:          0 :           if (pid > 0)
     811                 :          0 :             child = pid;
     812                 :            :           else
     813                 :            :             {
     814 [ #  # ][ #  # ]:          0 :               cerr << _F("failed to run the module on %s : ret=%d",
         [ #  # ][ #  # ]
     815         [ #  # ]:          0 :                          host.c_str(), pid) << endl;
     816                 :          0 :               rc = -1;
     817 [ #  # ][ #  # ]:          0 :             }
     818                 :            :         }
     819                 :            : 
     820 [ #  # ][ #  # ]:          0 :         return rc;
     821                 :            :       }
     822                 :            : 
     823                 :          0 :     int finish()
     824                 :            :       {
     825                 :          0 :         int rc = 0;
     826                 :            : 
     827         [ #  # ]:          0 :         if (child > 0)
     828                 :            :           {
     829                 :          0 :             rc = stap_waitpid(s->verbose, child);
     830                 :          0 :             child = 0;
     831                 :            :           }
     832                 :            : 
     833         [ #  # ]:          0 :         if (!tmpdir.empty())
     834                 :            :           {
     835                 :            :             // Remove the tempdir.
     836                 :            :             // XXX need to make sure this runs even with e.g. CTRL-C exits
     837         [ #  # ]:          0 :             vector<string> cmd = ssh_args;
     838 [ #  # ][ #  # ]:          0 :             cmd.push_back("-t");
                 [ #  # ]
     839 [ #  # ][ #  # ]:          0 :             cmd.push_back("rm -r " + cmdstr_quoted(tmpdir));
         [ #  # ][ #  # ]
                 [ #  # ]
     840         [ #  # ]:          0 :             int rc2 = stap_system(s->verbose, cmd);
     841         [ #  # ]:          0 :             if (rc2 != 0)
     842 [ #  # ][ #  # ]:          0 :               cerr << _F("failed to delete the tempdir on %s : rc=%d",
         [ #  # ][ #  # ]
     843         [ #  # ]:          0 :                          host.c_str(), rc2) << endl;
     844         [ #  # ]:          0 :             if (rc == 0)
     845                 :          0 :               rc = rc2;
     846 [ #  # ][ #  # ]:          0 :             tmpdir.clear();
     847                 :            :           }
     848                 :            : 
     849                 :          0 :         close_control_master();
     850                 :            : 
     851                 :          0 :         return rc;
     852                 :            :       }
     853                 :            : 
     854                 :            :   public:
     855                 :            :     friend class ssh_remote;
     856                 :            : 
     857                 :          0 :     virtual ~ssh_legacy_remote()
     858                 :          0 :       {
     859         [ #  # ]:          0 :         close_control_master();
     860 [ #  # ][ #  # ]:          0 :       }
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     861                 :            : };
     862                 :            : 
     863                 :            : 
     864                 :            : // Try to establish a stapsh connection to the remote, but fallback
     865                 :            : // to the older mechanism if the command is not found.
     866                 :            : remote*
     867                 :          0 : ssh_remote::create(systemtap_session& s, const string& target)
     868                 :            : {
     869 [ #  # ][ #  # ]:          0 :   string port, host = target;
     870         [ #  # ]:          0 :   size_t i = host.find(':');
     871         [ #  # ]:          0 :   if (i != string::npos)
     872                 :            :     {
     873 [ #  # ][ #  # ]:          0 :       port = host.substr(i + 1);
                 [ #  # ]
     874         [ #  # ]:          0 :       host.erase(i);
     875                 :            :     }
     876                 :            : 
     877 [ #  # ][ #  # ]:          0 :   auto_ptr<ssh_remote> p (new ssh_remote(s));
     878         [ #  # ]:          0 :   int rc = p->connect(host, port);
     879         [ #  # ]:          0 :   if (rc == 0)
     880                 :          0 :     return p.release();
     881         [ #  # ]:          0 :   else if (rc == 127) // stapsh command not found
     882 [ #  # ][ #  # ]:          0 :     return new ssh_legacy_remote(s, host, port); // try legacy instead
     883 [ #  # ][ #  # ]:          0 :   return NULL;
                 [ #  # ]
     884                 :            : }
     885                 :            : 
     886                 :            : remote*
     887                 :          0 : ssh_remote::create(systemtap_session& s, const uri_decoder& ud)
     888                 :            : {
     889 [ #  # ][ #  # ]:          0 :   if (!ud.has_authority || ud.authority.empty())
                 [ #  # ]
     890 [ #  # ][ #  # ]:          0 :     throw runtime_error(_("ssh target requires a hostname"));
     891 [ #  # ][ #  # ]:          0 :   if (!ud.path.empty() && ud.path != "/")
                 [ #  # ]
     892 [ #  # ][ #  # ]:          0 :     throw runtime_error(_("ssh target URI doesn't support a /path"));
     893         [ #  # ]:          0 :   if (ud.has_query)
     894 [ #  # ][ #  # ]:          0 :     throw runtime_error(_("ssh target URI doesn't support a ?query"));
     895         [ #  # ]:          0 :   if (ud.has_fragment)
     896 [ #  # ][ #  # ]:          0 :     throw runtime_error(_("ssh target URI doesn't support a #fragment"));
     897                 :            : 
     898                 :          0 :   return create(s, ud.authority);
     899                 :            : }
     900                 :            : 
     901                 :            : 
     902                 :            : remote*
     903                 :       2214 : remote::create(systemtap_session& s, const string& uri, int idx)
     904                 :            : {
     905                 :       2214 :   remote *it = 0;
     906                 :            :   try
     907                 :            :     {
     908 [ +  - ][ +  - ]:       2214 :       if (uri.find(':') != string::npos)
     909                 :            :         {
     910         [ +  - ]:       2214 :           const uri_decoder ud(uri);
     911                 :            : 
     912                 :            :           // An ssh "host:port" is ambiguous with a URI "scheme:path".
     913                 :            :           // So if it looks like a number, just assume ssh.
     914 [ +  - ][ +  - ]:       4429 :           if (!ud.has_authority && !ud.has_query &&
         [ +  - ][ +  + ]
         [ -  + ][ -  + ]
     915         [ +  - ]:       2214 :               !ud.has_fragment && !ud.path.empty() &&
     916         [ +  - ]:          1 :               ud.path.find_first_not_of("1234567890") == string::npos)
     917         [ #  # ]:          0 :             it = ssh_remote::create(s, uri);
     918 [ +  - ][ +  + ]:       2214 :           else if (ud.scheme == "direct")
     919 [ +  - ][ +  - ]:       2209 :             it = new direct(s);
     920 [ +  - ][ +  + ]:          5 :           else if (ud.scheme == "stapsh")
     921 [ +  - ][ +  - ]:          4 :             it = new direct_stapsh(s);
     922 [ +  - ][ +  - ]:          1 :           else if (ud.scheme == "unix")
     923 [ +  - ][ -  + ]:          1 :             it = new unix_stapsh(s, ud);
     924 [ #  # ][ #  # ]:          0 :           else if (ud.scheme == "ssh")
     925         [ #  # ]:          0 :             it = ssh_remote::create(s, ud);
     926                 :            :           else
     927                 :          0 :             throw runtime_error(_F("unrecognized URI scheme '%s' in remote: %s",
     928 [ #  # ][ #  # ]:       2214 :                                    ud.scheme.c_str(), uri.c_str()));
         [ #  # ][ #  # ]
                 [ +  - ]
     929                 :            :         }
     930                 :            :       else
     931                 :            :         // XXX assuming everything else is ssh for now...
     932         [ #  # ]:          0 :         it = ssh_remote::create(s, uri);
     933                 :            :     }
     934         [ -  + ]:          2 :   catch (std::runtime_error& e)
     935                 :            :     {
     936 [ -  + ][ -  + ]:          1 :       cerr << e.what() << " on remote '" << uri << "'" << endl;
         [ -  + ][ -  + ]
                 [ -  + ]
     937                 :          1 :       it = 0;
     938                 :            :     }
     939                 :            : 
     940 [ +  + ][ +  + ]:       2214 :   if (it && idx >= 0) // PR13354: remote metadata for staprun -r IDX:URI
     941                 :            :     {
     942         [ +  - ]:          7 :       stringstream r_arg;
     943 [ +  - ][ +  - ]:          7 :       r_arg << idx << ":" << uri;
                 [ +  - ]
     944 [ +  - ][ +  - ]:          7 :       it->staprun_r_arg = r_arg.str();
         [ +  - ][ +  - ]
     945                 :            :     }
     946                 :            : 
     947                 :       2214 :   return it;
     948                 :            : }
     949                 :            : 
     950                 :            : int
     951                 :        475 : remote::run(const vector<remote*>& remotes)
     952                 :            : {
     953                 :            :   // NB: the first failure "wins"
     954                 :        475 :   int ret = 0, rc = 0;
     955                 :            : 
     956 [ +  + ][ +  - ]:        951 :   for (unsigned i = 0; i < remotes.size() && !pending_interrupts; ++i)
                 [ +  + ]
     957                 :            :     {
     958                 :        476 :       remote *r = remotes[i];
     959                 :        476 :       r->s->verbose = r->s->perpass_verbose[4];
     960         [ -  + ]:        476 :       if (r->s->use_remote_prefix)
     961 [ #  # ][ #  # ]:          0 :         r->prefix = lex_cast(i) + ": ";
         [ #  # ][ #  # ]
                 [ #  # ]
     962         [ +  - ]:        476 :       rc = r->prepare();
     963         [ -  + ]:        476 :       if (rc)
     964                 :          0 :         return rc;
     965                 :            :     }
     966                 :            : 
     967 [ +  + ][ +  - ]:        951 :   for (unsigned i = 0; i < remotes.size() && !pending_interrupts; ++i)
                 [ +  + ]
     968                 :            :     {
     969                 :        476 :       rc = remotes[i]->start();
     970         [ +  - ]:        476 :       if (!ret)
     971                 :        476 :         ret = rc;
     972                 :            :     }
     973                 :            : 
     974                 :            :   // mask signals while we're preparing to poll
     975                 :            :   {
     976                 :        475 :     stap_sigmasker masked;
     977                 :            : 
     978                 :            :     // polling loop for remotes that have fds to watch
     979                 :         16 :     for (;;)
     980                 :            :       {
     981         [ +  - ]:        491 :         vector<pollfd> fds;
     982         [ +  + ]:        988 :         for (unsigned i = 0; i < remotes.size(); ++i)
     983         [ +  - ]:        497 :           remotes[i]->prepare_poll (fds);
     984 [ +  - ][ +  + ]:        491 :         if (fds.empty())
     985                 :            :           break;
     986                 :            : 
     987         [ +  - ]:         16 :         rc = ppoll (&fds[0], fds.size(), NULL, &masked.old);
     988 [ +  + ][ -  + ]:         16 :         if (rc < 0 && errno != EINTR)
     989                 :            :           break;
     990                 :            : 
     991         [ +  + ]:         37 :         for (unsigned i = 0; i < remotes.size(); ++i)
     992         [ +  - ]:         21 :           remotes[i]->handle_poll (fds);
     993 [ +  - ][ +  + ]:        491 :       }
     994                 :            :   }
     995                 :            : 
     996         [ +  + ]:        951 :   for (unsigned i = 0; i < remotes.size(); ++i)
     997                 :            :     {
     998                 :        476 :       rc = remotes[i]->finish();
     999         [ +  - ]:        476 :       if (!ret)
    1000                 :        476 :         ret = rc;
    1001                 :            :     }
    1002                 :            : 
    1003                 :        475 :   return ret;
    1004 [ +  - ][ +  - ]:       7242 : }
    1005                 :            : 
    1006                 :            : 
    1007                 :            : /* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */

Generated by: LCOV version 1.9