Switch to unified view

a/src/cdplugins/cmdtalk.cpp b/src/cdplugins/cmdtalk.cpp
1
/home/dockes/projets/docklib/cmdtalk/cmdtalk.cpp
1
/* Copyright (C) 2016 J.F.Dockes 
2
 *   This program is free software; you can redistribute it and/or modify
3
 *   it under the terms of the GNU General Public License as published by
4
 *   the Free Software Foundation; either version 2 of the License, or
5
 *   (at your option) any later version.
6
 *
7
 *   This program is distributed in the hope that it will be useful,
8
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10
 *   GNU General Public License for more details.
11
 *
12
 *   You should have received a copy of the GNU General Public License
13
 *   along with this program; if not, write to the
14
 *   Free Software Foundation, Inc.,
15
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
16
 */
17
#include "cmdtalk.h"
18
19
#include <stdio.h>
20
21
#include <iostream>
22
#include <sstream>
23
#include <mutex>
24
25
#include "smallut.h"
26
#include "execmd.h"
27
#include "log.h"
28
29
using namespace std;
30
31
class CmdTalk::Internal {
32
public:
33
    Internal()
34
  : cmd(0) {
35
    }
36
    ~Internal() {
37
  delete cmd;
38
    }
39
    bool readDataElement(string& name, string &data);
40
41
    bool talk(const pair<string, string>& arg0,
42
        const unordered_map<string, string>& args,
43
        unordered_map<string, string>& rep);
44
    ExecCmd *cmd;
45
    std::mutex mmutex;
46
};
47
48
CmdTalk::CmdTalk()
49
{
50
    m = new Internal;
51
}
52
CmdTalk::~CmdTalk()
53
{
54
    delete m;
55
}
56
57
bool CmdTalk::startCmd(const string& cmdname,
58
             const vector<string>& args,
59
             const vector<string>& env,
60
             const vector<string>& path)
61
{
62
    LOGDEB("CmdTalk::startCmd\n" );
63
64
    delete m->cmd;
65
    m->cmd = new ExecCmd;
66
    
67
    for (const auto& it : env) {
68
  m->cmd->putenv(it);
69
    }
70
71
    string acmdname(cmdname);
72
    if (!path.empty()) {
73
  string colonpath;
74
  for (const auto& it: path) {
75
      colonpath += it + ":";
76
  }
77
  if (!colonpath.empty()) {
78
      colonpath.erase(colonpath.size()-1);
79
  }
80
  LOGDEB("CmdTalk::startCmd: PATH: [" << colonpath << "]\n");
81
  ExecCmd::which(cmdname, acmdname, colonpath.c_str());
82
    }
83
84
    if (m->cmd->startExec(acmdname, args, 1, 1) < 0) {
85
        return false;
86
    }
87
    return true;
88
}
89
90
// Messages are made of data elements. Each element is like:
91
// name: len\ndata
92
// An empty line signals the end of the message, so the whole thing
93
// would look like:
94
// Name1: Len1\nData1Name2: Len2\nData2\n
95
bool CmdTalk::Internal::readDataElement(string& name, string &data)
96
{
97
    string ibuf;
98
99
    // Read name and length
100
    if (cmd->getline(ibuf) <= 0) {
101
        LOGERR("CmdTalk: getline error\n" );
102
        return false;
103
    }
104
    
105
    LOGDEB1("CmdTalk:rde: line ["  << (ibuf) << "]\n" );
106
107
    // Empty line (end of message) ?
108
    if (!ibuf.compare("\n")) {
109
        LOGDEB("CmdTalk: Got empty line\n" );
110
        return true;
111
    }
112
113
    // We're expecting something like Name: len\n
114
    vector<string> tokens;
115
    stringToTokens(ibuf, tokens);
116
    if (tokens.size() != 2) {
117
        LOGERR("CmdTalk: bad line in filter output: ["  << (ibuf) << "]\n" );
118
        return false;
119
    }
120
    vector<string>::iterator it = tokens.begin();
121
    name = *it++;
122
    string& slen = *it;
123
    int len;
124
    if (sscanf(slen.c_str(), "%d", &len) != 1) {
125
        LOGERR("CmdTalk: bad line in filter output: ["  << (ibuf) << "]\n" );
126
        return false;
127
    }
128
129
    // Read element data
130
    data.erase();
131
    if (len > 0 && cmd->receive(data, len) != len) {
132
        LOGERR("CmdTalk: expected " << len << " bytes of data, got " <<
133
         data.length() << "\n");
134
        return false;
135
    }
136
    LOGDEB1("CmdTalk:rde: got: name [" << name << "] len " << len <<"value ["<<
137
      (data.size() > 100 ? (data.substr(0, 100) + " ...") : data)<< endl);
138
    return true;
139
}
140
141
bool CmdTalk::Internal::talk(const pair<string, string>& arg0,
142
               const unordered_map<string, string>& args,
143
               unordered_map<string, string>& rep)
144
{
145
    std::unique_lock<std::mutex> lock(mmutex);
146
    if (cmd->getChildPid() <= 0) {
147
  LOGERR("CmdTalk::talk: no process\n");
148
        return false;
149
    }
150
151
    ostringstream obuf;
152
    if (!arg0.first.empty()) {
153
        obuf << arg0.first << ": " << arg0.second.size() << "\n" << arg0.second;
154
    }
155
    for (const auto& it : args) {
156
        obuf << it.first << ": " << it.second.size() << "\n" << it.second;
157
    }
158
    obuf << "\n";
159
160
    if (cmd->send(obuf.str()) < 0) {
161
        cmd->zapChild();
162
        LOGERR("CmdTalk: send error\n" );
163
        return false;
164
    }
165
166
    // Read answer (multiple elements)
167
    LOGDEB1("CmdTalk: reading answer\n" );
168
    for (;;) {
169
        string name, data;
170
  if (!readDataElement(name, data)) {
171
      cmd->zapChild();
172
      return false;
173
  }
174
        if (name.empty()) {
175
            break;
176
  }
177
  trimstring(name, ":");
178
  LOGDEB("CmdTalk: got [" << name << "] -> [" << data << "]\n");
179
  rep[name] = data;
180
    }
181
182
    if (rep.find("cmdtalkstatus") != rep.end()) {
183
  return false;
184
    } else {
185
  return true;
186
    }
187
}
188
189
bool CmdTalk::running()
190
{
191
    return m && m->cmd && m->cmd->getChildPid() > 0;
192
}
193
194
bool CmdTalk::talk(const unordered_map<string, string>& args,
195
         unordered_map<string, string>& rep)
196
{
197
    return m->talk({"",""}, args, rep);
198
}
199
200
bool CmdTalk::callproc(
201
  const string& proc,
202
  const unordered_map<std::string, std::string>& args,
203
  unordered_map<std::string, std::string>& rep)
204
{
205
    return m->talk({"cmdtalk:proc", proc}, args, rep);
206
}
207
208