Switch to unified view

a/src/execmd.cpp b/src/execmd.cpp
...
...
13
 *   along with this program; if not, write to the
13
 *   along with this program; if not, write to the
14
 *   Free Software Foundation, Inc.,
14
 *   Free Software Foundation, Inc.,
15
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
15
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
16
 */
16
 */
17
#ifndef TEST_EXECMD
17
#ifndef TEST_EXECMD
18
#ifdef BUILDING_RECOLL
19
#include "autoconfig.h"
20
#else
18
#include "config.h"
21
#include "config.h"
22
#endif
19
23
20
#include <stdio.h>
24
#include <stdio.h>
21
#include <stdlib.h>
25
#include <stdlib.h>
22
#include <unistd.h>
26
#include <unistd.h>
23
#include <sys/stat.h>
27
#include <sys/stat.h>
24
#include <sys/types.h>
28
#include <sys/types.h>
25
#include <sys/wait.h>
29
#include <sys/wait.h>
26
#include <sys/select.h>
30
#include <sys/select.h>
31
#include <sys/time.h>
32
#include <sys/resource.h>
27
#include <fcntl.h>
33
#include <fcntl.h>
28
#include <errno.h>
34
#include <errno.h>
29
#include <signal.h>
35
#include <signal.h>
30
#include <time.h>
36
#include <time.h>
31
37
32
#include <vector>
38
#include <vector>
33
#include <string>
39
#include <string>
40
#include <stdexcept>
41
#ifdef HAVE_SPAWN_H
42
#ifndef __USE_GNU
43
#define __USE_GNU
44
#define undef__USE_GNU
45
#endif
46
#include <spawn.h>
47
#ifdef undef__USE_GNU
48
#undef __USE_GNU
49
#endif
50
#endif
34
51
35
#include "execmd.h"
52
#include "execmd.h"
36
53
37
#include "netcon.h"
54
#include "netcon.h"
38
#include "closefrom.h"
55
#include "closefrom.h"
56
#include "smallut.h"
39
57
40
using namespace std;
58
using namespace std;
41
59
42
extern char **environ;
60
extern char **environ;
43
61
44
bool ExecCmd::o_useVfork = false;
62
#ifdef BUILDING_RECOLL
45
46
#ifdef RECOLL_DATADIR
47
#include "debuglog.h"
63
#include "debuglog.h"
48
#include "smallut.h"
49
64
50
#else
65
#else
51
// If compiling outside of recoll, make the file as standalone as reasonable.
66
// If compiling outside of recoll, make the file as standalone as reasonable.
52
67
53
#define LOGFATAL(X)
68
#define LOGFATAL(X)
...
...
58
#define LOGDEB1(X)
73
#define LOGDEB1(X)
59
#define LOGDEB2(X)
74
#define LOGDEB2(X)
60
#define LOGDEB3(X)
75
#define LOGDEB3(X)
61
#define LOGDEB4(X)
76
#define LOGDEB4(X)
62
77
63
#ifndef MIN
78
#endif // BUILDING_RECOLL
64
#define MIN(A,B) ((A) < (B) ? (A) : (B))
65
#endif
66
79
67
static void stringToTokens(const string &s, vector<string> &tokens, 
80
class ExecCmd::Internal {
68
                           const string &delims = " \t", bool skipinit=true);
81
public:
69
82
    Internal()
70
static void stringToTokens(const string& str, vector<string>& tokens,
83
        : m_advise(0), m_provide(0), m_timeoutMs(1000),
71
                           const string& delims, bool skipinit)
84
          m_rlimit_as_mbytes(0) {
72
{
73
    string::size_type startPos = 0, pos;
74
75
    // Skip initial delims, return empty if this eats all.
76
    if (skipinit && 
77
  (startPos = str.find_first_not_of(delims, 0)) == string::npos) {
78
  return;
79
    }
85
    }
80
    while (startPos < str.size()) { 
81
        // Find next delimiter or end of string (end of token)
82
        pos = str.find_first_of(delims, startPos);
83
86
84
        // Add token to the vector and adjust start
87
    static bool      o_useVfork;
85
  if (pos == string::npos) {
88
86
      tokens.push_back(str.substr(startPos));
89
    std::vector<std::string>   m_env;
87
      break;
90
    ExecCmdAdvise   *m_advise;
88
  } else if (pos == startPos) {
91
    ExecCmdProvide  *m_provide;
89
      // Dont' push empty tokens after first
92
    bool             m_killRequest;
90
      if (tokens.empty())
93
    int              m_timeoutMs;
91
      tokens.push_back(string());
94
    int              m_rlimit_as_mbytes;
92
      startPos = ++pos;
95
    string           m_stderrFile;
93
  } else {
96
    // Pipe for data going to the command
94
      tokens.push_back(str.substr(startPos, pos - startPos));
97
    int              m_pipein[2];
95
      startPos = ++pos;
98
    STD_SHARED_PTR<NetconCli> m_tocmd;
96
  }
99
    // Pipe for data coming out
100
    int              m_pipeout[2];
101
    STD_SHARED_PTR<NetconCli> m_fromcmd;
102
    // Subprocess id
103
    pid_t            m_pid;
104
    // Saved sigmask
105
    sigset_t         m_blkcld;
106
107
    // Reset internal state indicators. Any resources should have been
108
    // previously freed
109
    void reset() {
110
        m_killRequest = false;
111
        m_pipein[0] = m_pipein[1] = m_pipeout[0] = m_pipeout[1] = -1;
112
        m_pid = -1;
113
        sigemptyset(&m_blkcld);
97
    }
114
    }
115
    // Child process code
116
    inline void dochild(const std::string& cmd, const char **argv,
117
                        const char **envv, bool has_input, bool has_output);
98
}
118
};
99
#endif // RECOLL_DATADIR
119
bool ExecCmd::Internal::o_useVfork = false;
120
121
ExecCmd::ExecCmd(int)
122
{
123
    m = new Internal();
124
    if (m) {
125
        m->reset();
126
    }
127
}
128
void ExecCmd::setAdvise(ExecCmdAdvise *adv)
129
{
130
    m->m_advise = adv;
131
}
132
void ExecCmd::setProvide(ExecCmdProvide *p)
133
{
134
    m->m_provide = p;
135
}
136
void ExecCmd::setTimeout(int mS)
137
{
138
    if (mS > 30) {
139
        m->m_timeoutMs = mS;
140
    }
141
}
142
void ExecCmd::setStderr(const std::string& stderrFile)
143
{
144
    m->m_stderrFile = stderrFile;
145
}
146
pid_t ExecCmd::getChildPid()
147
{
148
    return m->m_pid;
149
}
150
void ExecCmd::setKill()
151
{
152
    m->m_killRequest = true;
153
}
154
void ExecCmd::zapChild()
155
{
156
    setKill();
157
    (void)wait();
158
}
159
160
bool ExecCmd::requestChildExit()
161
{
162
    if (m->m_pid > 0) {
163
        if (kill(m->m_pid, SIGTERM) == 0) {
164
            return true;
165
        }
166
    }
167
    return false;
168
}
100
169
101
/* From FreeBSD's which command */
170
/* From FreeBSD's which command */
102
static bool exec_is_there(const char *candidate)
171
static bool exec_is_there(const char *candidate)
103
{
172
{
104
    struct stat fin;
173
    struct stat fin;
105
174
106
    /* XXX work around access(2) false positives for superuser */
175
    /* XXX work around access(2) false positives for superuser */
107
    if (access(candidate, X_OK) == 0 &&
176
    if (access(candidate, X_OK) == 0 &&
108
  stat(candidate, &fin) == 0 &&
177
            stat(candidate, &fin) == 0 &&
109
  S_ISREG(fin.st_mode) &&
178
            S_ISREG(fin.st_mode) &&
110
  (getuid() != 0 ||
179
            (getuid() != 0 ||
111
   (fin.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0)) {
180
             (fin.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0)) {
112
  return true;
181
        return true;
113
    }
182
    }
114
    return false;
183
    return false;
115
}
184
}
116
185
117
bool ExecCmd::which(const string& cmd, string& exepath, const char* path)
186
bool ExecCmd::which(const string& cmd, string& exepath, const char* path)
118
{
187
{
119
    if (cmd.empty()) 
188
    if (cmd.empty()) {
120
  return false;
189
        return false;
190
    }
121
    if (cmd[0] == '/') {
191
    if (cmd[0] == '/') {
122
  if (exec_is_there(cmd.c_str())) {
192
        if (exec_is_there(cmd.c_str())) {
123
      exepath = cmd;
193
            exepath = cmd;
124
      return true;
194
            return true;
125
  } else {
195
        } else {
126
      return false;
196
            return false;
127
  }
197
        }
128
    }
198
    }
129
199
130
    const char *pp;
200
    const char *pp;
131
    if (path) {
201
    if (path) {
132
  pp = path;
202
        pp = path;
133
    } else {
203
    } else {
134
  pp = getenv("PATH");
204
        pp = getenv("PATH");
135
    }
205
    }
136
    if (pp == 0)
206
    if (pp == 0) {
137
  return false;
207
        return false;
208
    }
138
209
139
    vector<string> pels;
210
    vector<string> pels;
140
    stringToTokens(pp, pels, ":");
211
    stringToTokens(pp, pels, ":");
141
    for (vector<string>::iterator it = pels.begin(); it != pels.end(); it++) {
212
    for (vector<string>::iterator it = pels.begin(); it != pels.end(); it++) {
142
  if (it->empty())
213
        if (it->empty()) {
143
      *it = ".";
214
            *it = ".";
215
        }
144
  string candidate = (it->empty() ? string(".") : *it) + "/" + cmd;
216
        string candidate = (it->empty() ? string(".") : *it) + "/" + cmd;
145
  if (exec_is_there(candidate.c_str())) {
217
        if (exec_is_there(candidate.c_str())) {
146
      exepath = candidate;
218
            exepath = candidate;
147
      return true;
219
            return true;
148
  }
220
        }
149
    }
221
    }
150
    return false;
222
    return false;
151
}
223
}
152
224
225
void ExecCmd::useVfork(bool on)
226
{
227
    // Just in case: there are competent people who believe that the
228
    // dynamic linker can sometimes deadlock if execve() is resolved
229
    // inside the vfork/exec window. Make sure it's done now. If "/" is
230
    // an executable file, we have a problem.
231
    const char *argv[] = {"/", 0};
232
    execve("/", (char *const *)argv, environ);
233
    Internal::o_useVfork  = on;
234
}
235
153
void  ExecCmd::putenv(const string &ea)
236
void ExecCmd::putenv(const string& ea)
154
{
237
{
155
    m_env.push_back(ea);
238
    m->m_env.push_back(ea);
156
}
239
}
157
240
158
void  ExecCmd::putenv(const string &name, const string& value)
241
void  ExecCmd::putenv(const string& name, const string& value)
159
{
242
{
160
    string ea = name + "=" + value;
243
    string ea = name + "=" + value;
161
    putenv(ea);
244
    putenv(ea);
162
}
245
}
163
246
...
...
167
    spec.tv_sec = millis / 1000;
250
    spec.tv_sec = millis / 1000;
168
    spec.tv_nsec = (millis % 1000) * 1000000;
251
    spec.tv_nsec = (millis % 1000) * 1000000;
169
    nanosleep(&spec, 0);
252
    nanosleep(&spec, 0);
170
}
253
}
171
254
172
/** A resource manager to ensure that execcmd cleans up if an exception is 
255
/** A resource manager to ensure that execcmd cleans up if an exception is
173
 *  raised in the callback, or at different places on errors occurring
256
 *  raised in the callback, or at different places on errors occurring
174
 *  during method executions */
257
 *  during method executions */
175
class ExecCmdRsrc {
258
class ExecCmdRsrc {
176
public:
259
public:
177
    ExecCmdRsrc(ExecCmd *parent) : m_parent(parent), m_active(true) {}
260
    ExecCmdRsrc(ExecCmd::Internal *parent)
178
    void inactivate() {m_active = false;}
261
        : m_parent(parent), m_active(true) {
262
    }
263
    void inactivate() {
264
        m_active = false;
265
    }
179
    ~ExecCmdRsrc() {
266
    ~ExecCmdRsrc() {
180
  if (!m_active || !m_parent)
267
        if (!m_active || !m_parent) {
181
      return;
268
            return;
269
        }
182
  LOGDEB1(("~ExecCmdRsrc: working. mypid: %d\n", (int)getpid()));
270
        LOGDEB1(("~ExecCmdRsrc: working. mypid: %d\n", (int)getpid()));
183
271
184
  // Better to close the descs first in case the child is waiting in read
272
        // Better to close the descs first in case the child is waiting in read
185
  if (m_parent->m_pipein[0] >= 0)
273
        if (m_parent->m_pipein[0] >= 0) {
186
      close(m_parent->m_pipein[0]);
274
            close(m_parent->m_pipein[0]);
275
        }
187
  if (m_parent->m_pipein[1] >= 0)
276
        if (m_parent->m_pipein[1] >= 0) {
188
      close(m_parent->m_pipein[1]);
277
            close(m_parent->m_pipein[1]);
278
        }
189
  if (m_parent->m_pipeout[0] >= 0)
279
        if (m_parent->m_pipeout[0] >= 0) {
190
      close(m_parent->m_pipeout[0]);
280
            close(m_parent->m_pipeout[0]);
281
        }
191
  if (m_parent->m_pipeout[1] >= 0)
282
        if (m_parent->m_pipeout[1] >= 0) {
192
      close(m_parent->m_pipeout[1]);
283
            close(m_parent->m_pipeout[1]);
284
        }
193
285
194
  // It's apparently possible for m_pid to be > 0 and getpgid to fail. In
286
        // It's apparently possible for m_pid to be > 0 and getpgid to fail. In
195
  // this case, we have to conclude that the child process does 
287
        // this case, we have to conclude that the child process does
196
  // not exist. Not too sure what causes this, but the previous code
288
        // not exist. Not too sure what causes this, but the previous code
197
  // definitely tried to call killpg(-1,) from time to time.
289
        // definitely tried to call killpg(-1,) from time to time.
198
  pid_t grp;
290
        pid_t grp;
199
  if (m_parent->m_pid > 0 && (grp = getpgid(m_parent->m_pid)) > 0) {
291
        if (m_parent->m_pid > 0 && (grp = getpgid(m_parent->m_pid)) > 0) {
200
      LOGDEB(("ExecCmd: killpg(%d, SIGTERM)\n", grp));
292
            LOGDEB(("ExecCmd: killpg(%d, SIGTERM)\n", grp));
201
            int ret = killpg(grp, SIGTERM);
293
            int ret = killpg(grp, SIGTERM);
202
      if (ret == 0) {
294
            if (ret == 0) {
203
      for (int i = 0; i < 3; i++) {
295
                for (int i = 0; i < 3; i++) {
204
          msleep(i == 0 ? 5 : (i == 1 ? 100 : 2000));
296
                    msleep(i == 0 ? 5 : (i == 1 ? 100 : 2000));
205
          int status;
297
                    int status;
206
          (void)waitpid(m_parent->m_pid, &status, WNOHANG);
298
                    (void)waitpid(m_parent->m_pid, &status, WNOHANG);
207
          if (kill(m_parent->m_pid, 0) != 0)
299
                    if (kill(m_parent->m_pid, 0) != 0) {
208
          break;
300
                        break;
209
          if (i == 2) {
301
                    }
302
                    if (i == 2) {
210
          LOGDEB(("ExecCmd: killpg(%d, SIGKILL)\n", grp));
303
                        LOGDEB(("ExecCmd: killpg(%d, SIGKILL)\n", grp));
211
          killpg(grp, SIGKILL);
304
                        killpg(grp, SIGKILL);
212
          (void)waitpid(m_parent->m_pid, &status, WNOHANG);
305
                        (void)waitpid(m_parent->m_pid, &status, WNOHANG);
213
          }
306
                    }
214
      }
307
                }
215
      } else {
308
            } else {
216
                LOGERR(("ExecCmd: error killing process group %d: %d\n",
309
                LOGERR(("ExecCmd: error killing process group %d: %d\n",
217
                        grp, errno));
310
                        grp, errno));
218
            }
311
            }
219
  }
312
        }
220
  m_parent->m_tocmd.reset();
313
        m_parent->m_tocmd.reset();
221
  m_parent->m_fromcmd.reset();
314
        m_parent->m_fromcmd.reset();
222
  pthread_sigmask(SIG_UNBLOCK, &m_parent->m_blkcld, 0);
315
        pthread_sigmask(SIG_UNBLOCK, &m_parent->m_blkcld, 0);
223
  m_parent->reset();
316
        m_parent->reset();
224
    }
317
    }
225
private:
318
private:
226
    ExecCmd *m_parent;
319
    ExecCmd::Internal *m_parent;
227
    bool    m_active;
320
    bool    m_active;
228
};
321
};
229
322
230
ExecCmd::~ExecCmd()
323
ExecCmd::~ExecCmd()
231
{
324
{
232
    ExecCmdRsrc(this);
325
    ExecCmdRsrc(this->m);
326
    if (m) {
327
        delete m;
328
    }
233
}
329
}
234
330
235
// In child process. Set up pipes and exec command. 
331
// In child process. Set up pipes and exec command.
236
// This must not return. _exit() on error.
332
// This must not return. _exit() on error.
237
// *** This can be called after a vfork, so no modification of the
333
// *** This can be called after a vfork, so no modification of the
238
//     process memory at all is allowed ***
334
//     process memory at all is allowed ***
239
// The LOGXX calls should not be there, but they occur only after "impossible"
335
// The LOGXX calls should not be there, but they occur only after "impossible"
240
// errors, which we would most definitely want to have a hint about.
336
// errors, which we would most definitely want to have a hint about.
241
//
337
//
242
// Note that any of the LOGXX calls could block on a mutex set in the
338
// Note that any of the LOGXX calls could block on a mutex set in the
243
// father process, so that only absolutely exceptional conditions, 
339
// father process, so that only absolutely exceptional conditions,
244
// should be logged, for debugging and post-mortem purposes
340
// should be logged, for debugging and post-mortem purposes
245
// If one of the calls block, the problem manifests itself by 20mn
341
// If one of the calls block, the problem manifests itself by 20mn
246
// (filter timeout) of looping on "ExecCmd::doexec: selectloop
342
// (filter timeout) of looping on "ExecCmd::doexec: selectloop
247
// returned 1', because the father is waiting on the read descriptor
343
// returned 1', because the father is waiting on the read descriptor
248
inline void ExecCmd::dochild(const string &cmd, const char **argv,
344
inline void ExecCmd::Internal::dochild(const string& cmd, const char **argv,
249
               const char **envv,
345
                                       const char **envv,
250
               bool has_input, bool has_output)
346
                                       bool has_input, bool has_output)
251
{
347
{
252
    // Start our own process group
348
    // Start our own process group
253
    if (setpgid(0, getpid())) {
349
    if (setpgid(0, getpid())) {
254
  LOGINFO(("ExecCmd::DOCHILD: setpgid(0, %d) failed: errno %d\n",
350
        LOGINFO(("ExecCmd::DOCHILD: setpgid(0, %d) failed: errno %d\n",
255
       getpid(), errno));
351
                 getpid(), errno));
256
    }
352
    }
257
353
258
    // Restore SIGTERM to default. Really, signal handling should be
354
    // Restore SIGTERM to default. Really, signal handling should be
259
    // specified when creating the execmd. Help Recoll get rid of its
355
    // specified when creating the execmd, there might be other
356
    // signals to reset. Resetting SIGTERM helps Recoll get rid of its
260
    // filter children though. To be fixed one day... Not sure that
357
    // filter children for now though. To be fixed one day...
261
    // all of this is needed. But an ignored sigterm and the masks are
358
    // Note that resetting to SIG_DFL is a portable use of
262
    // normally inherited.
359
    // signal(). No need for sigaction() here.
360
361
    // There is supposedely a risk of problems if another thread was
362
    // calling a signal-affecting function when vfork was called. This
363
    // seems acceptable though as no self-respecting thread is going
364
    // to mess with the global process signal disposition.
365
263
    if (signal(SIGTERM, SIG_DFL) == SIG_ERR) {
366
    if (signal(SIGTERM, SIG_DFL) == SIG_ERR) {
264
  //LOGERR(("ExecCmd::DOCHILD: signal() failed, errno %d\n", errno));
367
        //LOGERR(("ExecCmd::DOCHILD: signal() failed, errno %d\n", errno));
265
    }
368
    }
266
    sigset_t sset;
369
    sigset_t sset;
267
    sigfillset(&sset);
370
    sigfillset(&sset);
268
    pthread_sigmask(SIG_UNBLOCK, &sset, 0);
371
    pthread_sigmask(SIG_UNBLOCK, &sset, 0);
269
    sigprocmask(SIG_UNBLOCK, &sset, 0);
372
    sigprocmask(SIG_UNBLOCK, &sset, 0);
270
373
374
#ifdef HAVE_SETRLIMIT
375
#if defined RLIMIT_AS || defined RLIMIT_VMEM || defined RLIMIT_DATA
376
    if (m_rlimit_as_mbytes > 2000 && sizeof(rlim_t) < 8) {
377
        // Impossible limit, don't use it
378
        m_rlimit_as_mbytes = 0;
379
    }
380
    if (m_rlimit_as_mbytes > 0) {
381
        struct rlimit ram_limit = {
382
            static_cast<rlim_t>(m_rlimit_as_mbytes * 1024 * 1024),
383
            RLIM_INFINITY
384
        };
385
        int resource;
386
387
        // RLIMIT_AS and RLIMIT_VMEM are usually synonyms when VMEM is
388
        // defined. RLIMIT_AS is Posix. Both don't really do what we
389
        // want, because they count e.g. shared lib mappings, which we
390
        // don't really care about.
391
        // RLIMIT_DATA only limits the data segment. Modern mallocs
392
        // use mmap and will not be bound. (Otoh if we only have this,
393
        // we're probably not modern).
394
        // So we're unsatisfied either way.
395
#ifdef RLIMIT_AS
396
        resource = RLIMIT_AS;
397
#elif defined RLIMIT_VMEM
398
        resource = RLIMIT_VMEM;
399
#else
400
        resource = RLIMIT_DATA;
401
#endif
402
        setrlimit(resource, &ram_limit);
403
    }
404
#endif
405
#endif // have_setrlimit
406
271
    if (has_input) {
407
    if (has_input) {
272
  close(m_pipein[1]);
273
  if (m_pipein[0] != 0) {
274
      dup2(m_pipein[0], 0);
275
      close(m_pipein[0]);
408
        close(m_pipein[1]);
276
  }
409
        if (m_pipein[0] != 0) {
410
            dup2(m_pipein[0], 0);
411
            close(m_pipein[0]);
412
        }
277
    }
413
    }
278
    if (has_output) {
414
    if (has_output) {
279
  close(m_pipeout[0]);
415
        close(m_pipeout[0]);
280
  if (m_pipeout[1] != 1) {
416
        if (m_pipeout[1] != 1) {
281
      if (dup2(m_pipeout[1], 1) < 0) {
417
            if (dup2(m_pipeout[1], 1) < 0) {
282
      LOGERR(("ExecCmd::DOCHILD: dup2() failed. errno %d\n", errno));
418
                LOGERR(("ExecCmd::DOCHILD: dup2() failed. errno %d\n", errno));
283
      }
419
            }
284
      if (close(m_pipeout[1]) < 0) {
420
            if (close(m_pipeout[1]) < 0) {
285
      LOGERR(("ExecCmd::DOCHILD: close() failed. errno %d\n", errno));
421
                LOGERR(("ExecCmd::DOCHILD: close() failed. errno %d\n", errno));
286
      }
422
            }
287
  }
423
        }
288
    }
424
    }
289
    // Do we need to redirect stderr ?
425
    // Do we need to redirect stderr ?
290
    if (!m_stderrFile.empty()) {
426
    if (!m_stderrFile.empty()) {
291
  int fd = open(m_stderrFile.c_str(), O_WRONLY|O_CREAT
427
        int fd = open(m_stderrFile.c_str(), O_WRONLY | O_CREAT
292
#ifdef O_APPEND
428
#ifdef O_APPEND
293
            |O_APPEND
429
                      | O_APPEND
294
#endif
430
#endif
295
            , 0600);
431
                      , 0600);
296
  if (fd < 0) {
432
        if (fd < 0) {
297
      close(2);
433
            close(2);
298
  } else {
434
        } else {
299
      if (fd != 2) {
435
            if (fd != 2) {
300
      dup2(fd, 2);
436
                dup2(fd, 2);
301
      }
437
            }
302
      lseek(2, 0, 2);
438
            lseek(2, 0, 2);
303
  }
439
        }
304
    }
440
    }
305
441
306
    // Close all descriptors except 0,1,2
442
    // Close all descriptors except 0,1,2
307
    libclf_closefrom(3);
443
    libclf_closefrom(3);
308
444
309
    execve(cmd.c_str(), (char *const*)argv, (char *const*)envv);
445
    execve(cmd.c_str(), (char *const*)argv, (char *const*)envv);
310
    // Hu ho. This should never happened as we checked the existence of the
446
    // Hu ho. This should never have happened as we checked the
311
    // executable before calling dochild... Until we did this, this was 
447
    // existence of the executable before calling dochild... Until we
312
    // the chief cause of LOG mutex deadlock
448
    // did this check, this was the chief cause of LOG mutex deadlock
313
    LOGERR(("ExecCmd::DOCHILD: execve(%s) failed. errno %d\n", cmd.c_str(),
449
    LOGERR(("ExecCmd::DOCHILD: execve(%s) failed. errno %d\n", cmd.c_str(),
314
      errno));
450
            errno));
315
    _exit(127);
451
    _exit(127);
316
}
452
}
317
453
454
void ExecCmd::setrlimit_as(int mbytes)
455
{
456
    m->m_rlimit_as_mbytes = mbytes;
457
}
458
318
int ExecCmd::startExec(const string &cmd, const vector<string>& args,
459
int ExecCmd::startExec(const string& cmd, const vector<string>& args,
319
             bool has_input, bool has_output)
460
                       bool has_input, bool has_output)
320
{
461
{
462
    {
321
    { // Debug and logging
463
        // Debug and logging
322
  string command = cmd + " ";
464
        string command = cmd + " ";
323
  for (vector<string>::const_iterator it = args.begin();
465
        for (vector<string>::const_iterator it = args.begin();
324
             it != args.end(); it++) {
466
                it != args.end(); it++) {
325
      command += "{" + *it + "} ";
467
            command += "{" + *it + "} ";
326
  }
468
        }
327
  LOGDEB(("ExecCmd::startExec: (%d|%d) %s\n", 
469
        LOGDEB(("ExecCmd::startExec: (%d|%d) %s\n",
328
      has_input, has_output, command.c_str()));
470
                has_input, has_output, command.c_str()));
329
    }
471
    }
330
472
331
    // The resource manager ensures resources are freed if we return early
473
    // The resource manager ensures resources are freed if we return early
332
    ExecCmdRsrc e(this);
474
    ExecCmdRsrc e(this->m);
333
475
334
    if (has_input && pipe(m_pipein) < 0) {
476
    if (has_input && pipe(m->m_pipein) < 0) {
335
  LOGERR(("ExecCmd::startExec: pipe(2) failed. errno %d\n", errno));
477
        LOGERR(("ExecCmd::startExec: pipe(2) failed. errno %d\n", errno));
336
  return -1;
478
        return -1;
337
    }
479
    }
338
    if (has_output && pipe(m_pipeout) < 0) {
480
    if (has_output && pipe(m->m_pipeout) < 0) {
339
  LOGERR(("ExecCmd::startExec: pipe(2) failed. errno %d\n", errno));
481
        LOGERR(("ExecCmd::startExec: pipe(2) failed. errno %d\n", errno));
340
  return -1;
482
        return -1;
341
    }
483
    }
342
484
343
485
344
//////////// vfork setup section
486
//////////// vfork setup section
345
    // We do here things that we could/should do after a fork(), but
487
    // We do here things that we could/should do after a fork(), but
...
...
348
    // exec()).
490
    // exec()).
349
491
350
    // Allocate arg vector (2 more for arg0 + final 0)
492
    // Allocate arg vector (2 more for arg0 + final 0)
351
    typedef const char *Ccharp;
493
    typedef const char *Ccharp;
352
    Ccharp *argv;
494
    Ccharp *argv;
353
    argv = (Ccharp *)malloc((args.size()+2) * sizeof(char *));
495
    argv = (Ccharp *)malloc((args.size() + 2) * sizeof(char *));
354
    if (argv == 0) {
496
    if (argv == 0) {
355
  LOGERR(("ExecCmd::doexec: malloc() failed. errno %d\n",  errno));
497
        LOGERR(("ExecCmd::doexec: malloc() failed. errno %d\n", errno));
356
        return -1;
498
        return -1;
357
    }
499
    }
358
    // Fill up argv
500
    // Fill up argv
359
    argv[0] = cmd.c_str();
501
    argv[0] = cmd.c_str();
360
    int i = 1;
502
    int i = 1;
361
    vector<string>::const_iterator it;
503
    vector<string>::const_iterator it;
362
    for (it = args.begin(); it != args.end(); it++) {
504
    for (it = args.begin(); it != args.end(); it++) {
363
  argv[i++] = it->c_str();
505
        argv[i++] = it->c_str();
364
    }
506
    }
365
    argv[i] = 0;
507
    argv[i] = 0;
366
508
367
    Ccharp *envv;
509
    Ccharp *envv;
368
    int envsize;
510
    int envsize;
369
    for (envsize = 0; ; envsize++) 
511
    for (envsize = 0; ; envsize++)
370
  if (environ[envsize] == 0)
512
        if (environ[envsize] == 0) {
371
      break;
513
            break;
514
        }
372
    envv = (Ccharp *)malloc((envsize + m_env.size() + 2) * sizeof(char *));
515
    envv = (Ccharp *)malloc((envsize + m->m_env.size() + 2) * sizeof(char *));
373
    if (envv == 0) {
516
    if (envv == 0) {
374
  LOGERR(("ExecCmd::doexec: malloc() failed. errno %d\n",  errno));
517
        LOGERR(("ExecCmd::doexec: malloc() failed. errno %d\n", errno));
375
        free(argv);
518
        free(argv);
376
        return -1;
519
        return -1;
377
    }
520
    }
378
    int eidx;
521
    int eidx;
379
    for (eidx = 0; eidx < envsize; eidx++)
522
    for (eidx = 0; eidx < envsize; eidx++) {
380
  envv[eidx] = environ[eidx];
523
        envv[eidx] = environ[eidx];
524
    }
381
    for (vector<string>::const_iterator it = m_env.begin(); 
525
    for (vector<string>::const_iterator it = m->m_env.begin();
382
   it != m_env.end(); it++) {
526
            it != m->m_env.end(); it++) {
383
  envv[eidx++] = it->c_str();
527
        envv[eidx++] = it->c_str();
384
    }
528
    }
385
    envv[eidx] = 0;
529
    envv[eidx] = 0;
386
530
387
    // As we are going to use execve, not execvp, do the PATH thing.
531
    // As we are going to use execve, not execvp, do the PATH thing.
388
    string exe;
532
    string exe;
...
...
390
        LOGERR(("ExecCmd::startExec: %s not found\n", cmd.c_str()));
534
        LOGERR(("ExecCmd::startExec: %s not found\n", cmd.c_str()));
391
        free(argv);
535
        free(argv);
392
        free(envv);
536
        free(envv);
393
        return -1;
537
        return -1;
394
    }
538
    }
395
////////////////////////////////
539
//////////////////////////////// End vfork child prepare section.
396
540
541
#if HAVE_POSIX_SPAWN && USE_POSIX_SPAWN
542
    // Note that posix_spawn provides no way to setrlimit() the child.
543
    {
544
        posix_spawnattr_t attrs;
545
        posix_spawnattr_init(&attrs);
546
        short flags;
547
        posix_spawnattr_getflags(&attrs, &flags);
548
549
        flags |=  POSIX_SPAWN_USEVFORK;
550
551
        posix_spawnattr_setpgroup(&attrs, 0);
552
        flags |= POSIX_SPAWN_SETPGROUP;
553
554
        sigset_t sset;
555
        sigemptyset(&sset);
556
        posix_spawnattr_setsigmask(&attrs, &sset);
557
        flags |= POSIX_SPAWN_SETSIGMASK;
558
559
        sigemptyset(&sset);
560
        sigaddset(&sset, SIGTERM);
561
        posix_spawnattr_setsigdefault(&attrs, &sset);
562
        flags |= POSIX_SPAWN_SETSIGDEF;
563
564
        posix_spawnattr_setflags(&attrs, flags);
565
566
        posix_spawn_file_actions_t facts;
567
        posix_spawn_file_actions_init(&facts);
568
569
        if (has_input) {
570
            posix_spawn_file_actions_addclose(&facts, m->m_pipein[1]);
571
            if (m->m_pipein[0] != 0) {
572
                posix_spawn_file_actions_adddup2(&facts, m->m_pipein[0], 0);
573
                posix_spawn_file_actions_addclose(&facts, m->m_pipein[0]);
574
            }
575
        }
576
        if (has_output) {
577
            posix_spawn_file_actions_addclose(&facts, m->m_pipeout[0]);
578
            if (m->m_pipeout[1] != 1) {
579
                posix_spawn_file_actions_adddup2(&facts, m->m_pipeout[1], 1);
580
                posix_spawn_file_actions_addclose(&facts, m->m_pipeout[1]);
581
            }
582
        }
583
584
        // Do we need to redirect stderr ?
585
        if (!m->m_stderrFile.empty()) {
586
            int oflags = O_WRONLY | O_CREAT;
587
#ifdef O_APPEND
588
            oflags |= O_APPEND;
589
#endif
590
            posix_spawn_file_actions_addopen(&facts, 2, m->m_stderrFile.c_str(),
591
                                             oflags, 0600);
592
        }
593
        LOGDEB1(("using SPAWN\n"));
594
595
        // posix_spawn() does not have any standard way to ask for
596
        // calling closefrom(). Afaik there is a solaris extension for this,
597
        // but let's just add all fds
598
        for (int i = 3; i < libclf_maxfd(); i++) {
599
            posix_spawn_file_actions_addclose(&facts, i);
600
        }
601
602
        int ret = posix_spawn(&m->m_pid, exe.c_str(), &facts, &attrs,
603
                              (char *const *)argv, (char *const *)envv);
604
        posix_spawnattr_destroy(&attrs);
605
        posix_spawn_file_actions_destroy(&facts);
606
        if (ret) {
607
            LOGERR(("ExecCmd::startExec: posix_spawn() failed. errno %d\n",
608
                    ret));
609
            return -1;
610
        }
611
    }
612
613
#else
397
    if (o_useVfork) {
614
    if (Internal::o_useVfork) {
398
  m_pid = vfork();
615
        LOGDEB1(("using VFORK\n"));
616
        m->m_pid = vfork();
399
    } else {
617
    } else {
400
  m_pid = fork();
618
        LOGDEB1(("using FORK\n"));
619
        m->m_pid = fork();
401
    }
620
    }
402
    if (m_pid < 0) {
621
    if (m->m_pid < 0) {
403
  LOGERR(("ExecCmd::startExec: fork(2) failed. errno %d\n", errno));
622
        LOGERR(("ExecCmd::startExec: fork(2) failed. errno %d\n", errno));
404
  return -1;
623
        return -1;
405
    }
624
    }
406
    if (m_pid == 0) {
625
    if (m->m_pid == 0) {
407
  // e.inactivate() is not needed. As we do not return, the call
626
        // e.inactivate() is not needed. As we do not return, the call
408
  // stack won't be unwound and destructors of local objects
627
        // stack won't be unwound and destructors of local objects
409
  // won't be called.
628
        // won't be called.
410
  dochild(exe, argv, envv, has_input, has_output);
629
        m->dochild(exe, argv, envv, has_input, has_output);
411
  // dochild does not return. Just in case...
630
        // dochild does not return. Just in case...
412
  _exit(1);
631
        _exit(1);
413
    }
632
    }
633
#endif
414
634
415
    // Father process
635
    // Father process
416
636
417
////////////////////
637
////////////////////
418
    // Vfork cleanup section
638
    // Vfork cleanup section
...
...
420
    free(envv);
640
    free(envv);
421
///////////////////
641
///////////////////
422
642
423
    // Set the process group for the child. This is also done in the
643
    // Set the process group for the child. This is also done in the
424
    // child process see wikipedia(Process_group)
644
    // child process see wikipedia(Process_group)
425
    if (setpgid(m_pid, m_pid)) {
645
    if (setpgid(m->m_pid, m->m_pid)) {
426
        // This can fail with EACCES if the son has already done execve 
646
        // This can fail with EACCES if the son has already done execve
427
        // (linux at least)
647
        // (linux at least)
428
        LOGDEB2(("ExecCmd: father setpgid(son)(%d,%d) errno %d (ok)\n",
648
        LOGDEB2(("ExecCmd: father setpgid(son)(%d,%d) errno %d (ok)\n",
429
                 m_pid, m_pid, errno));
649
                 m->m_pid, m->m_pid, errno));
430
    }
650
    }
431
651
432
    sigemptyset(&m_blkcld);
652
    sigemptyset(&m->m_blkcld);
433
    sigaddset(&m_blkcld, SIGCHLD);
653
    sigaddset(&m->m_blkcld, SIGCHLD);
434
    pthread_sigmask(SIG_BLOCK, &m_blkcld, 0);
654
    pthread_sigmask(SIG_BLOCK, &m->m_blkcld, 0);
435
655
436
    if (has_input) {
656
    if (has_input) {
437
  close(m_pipein[0]);
657
        close(m->m_pipein[0]);
438
  m_pipein[0] = -1;
658
        m->m_pipein[0] = -1;
439
  NetconCli *iclicon = new NetconCli();
659
        NetconCli *iclicon = new NetconCli();
440
  iclicon->setconn(m_pipein[1]);
660
        iclicon->setconn(m->m_pipein[1]);
441
  m_tocmd = NetconP(iclicon);
661
        m->m_tocmd = STD_SHARED_PTR<NetconCli>(iclicon);
442
    }
662
    }
443
    if (has_output) {
663
    if (has_output) {
444
  close(m_pipeout[1]);
664
        close(m->m_pipeout[1]);
445
  m_pipeout[1] = -1;
665
        m->m_pipeout[1] = -1;
446
  NetconCli *oclicon = new NetconCli();
666
        NetconCli *oclicon = new NetconCli();
447
  oclicon->setconn(m_pipeout[0]);
667
        oclicon->setconn(m->m_pipeout[0]);
448
  m_fromcmd = NetconP(oclicon);
668
        m->m_fromcmd = STD_SHARED_PTR<NetconCli>(oclicon);
449
    }
669
    }
450
670
451
    /* Don't want to undo what we just did ! */
671
    /* Don't want to undo what we just did ! */
452
    e.inactivate();
672
    e.inactivate();
453
673
...
...
455
}
675
}
456
676
457
// Netcon callback. Send data to the command's input
677
// Netcon callback. Send data to the command's input
458
class ExecWriter : public NetconWorker {
678
class ExecWriter : public NetconWorker {
459
public:
679
public:
460
    ExecWriter(const string *input, ExecCmdProvide *provide) 
680
    ExecWriter(const string *input, ExecCmdProvide *provide,
681
               ExecCmd::Internal *parent)
461
  : m_input(input), m_cnt(0), m_provide(provide)
682
        : m_cmd(parent), m_input(input), m_cnt(0), m_provide(provide) {
462
    {}                    
683
    }
684
    void shutdown() {
685
        close(m_cmd->m_pipein[1]);
686
        m_cmd->m_pipein[1] = -1;
687
        m_cmd->m_tocmd.reset();
688
    }
463
    virtual int data(NetconData *con, Netcon::Event reason)
689
    virtual int data(NetconData *con, Netcon::Event reason) {
464
    {
690
        if (!m_input) {
465
  if (!m_input) return -1;
691
            return -1;
692
        }
466
  LOGDEB1(("ExecWriter: input m_cnt %d input length %d\n", m_cnt, 
693
        LOGDEB1(("ExecWriter: input m_cnt %d input length %d\n", m_cnt,
467
       m_input->length()));
694
                 m_input->length()));
468
  if (m_cnt >= m_input->length()) {
695
        if (m_cnt >= m_input->length()) {
469
      // Fd ready for more but we got none.
696
            // Fd ready for more but we got none. Try to get data, else
697
            // shutdown;
470
      if (m_provide) {
698
            if (!m_provide) {
699
                shutdown();
700
                return 0;
701
            }
471
      m_provide->newData();
702
            m_provide->newData();
472
      if (m_input->empty()) {
703
            if (m_input->empty()) {
473
          return 0;
704
                shutdown();
474
      } else {
705
                return 0;
475
          m_cnt = 0;
706
            } else {
476
      }
707
                // Ready with new buffer, reset use count
708
                m_cnt = 0;
709
            }
477
      LOGDEB2(("ExecWriter: provide m_cnt %d input length %d\n", 
710
            LOGDEB2(("ExecWriter: provide m_cnt %d input length %d\n",
478
           m_cnt, m_input->length()));
711
                     m_cnt, m_input->length()));
479
      } else {
712
        }
480
      return 0;
481
      }
482
  }
483
  int ret = con->send(m_input->c_str() + m_cnt, 
713
        int ret = con->send(m_input->c_str() + m_cnt,
484
              m_input->length() - m_cnt);
714
                            m_input->length() - m_cnt);
485
  LOGDEB2(("ExecWriter: wrote %d to command\n", ret));
715
        LOGDEB2(("ExecWriter: wrote %d to command\n", ret));
486
  if (ret <= 0) {
716
        if (ret <= 0) {
487
      LOGERR(("ExecWriter: data: can't write\n"));
717
            LOGERR(("ExecWriter: data: can't write\n"));
488
      return -1;
718
            return -1;
489
  }
719
        }
490
  m_cnt += ret;
720
        m_cnt += ret;
491
  return ret;
721
        return ret;
492
    }
722
    }
493
private:
723
private:
724
    ExecCmd::Internal *m_cmd;
494
    const string   *m_input;
725
    const string   *m_input;
495
    unsigned int    m_cnt; // Current offset inside m_input
726
    unsigned int    m_cnt; // Current offset inside m_input
496
    ExecCmdProvide *m_provide;
727
    ExecCmdProvide *m_provide;
497
};
728
};
498
729
499
// Netcon callback. Get data from the command output.
730
// Netcon callback. Get data from the command output.
500
class ExecReader : public NetconWorker {
731
class ExecReader : public NetconWorker {
501
public:
732
public:
502
    ExecReader(string *output, ExecCmdAdvise *advise) 
733
    ExecReader(string *output, ExecCmdAdvise *advise)
503
  : m_output(output), m_advise(advise)
734
        : m_output(output), m_advise(advise) {
504
    {}                    
735
    }
505
    virtual int data(NetconData *con, Netcon::Event reason)
736
    virtual int data(NetconData *con, Netcon::Event reason) {
506
    {
507
  char buf[8192];
737
        char buf[8192];
508
  int n = con->receive(buf, 8192);
738
        int n = con->receive(buf, 8192);
509
  LOGDEB1(("ExecReader: got %d from command\n", n));
739
        LOGDEB1(("ExecReader: got %d from command\n", n));
510
  if (n < 0) {
740
        if (n < 0) {
511
      LOGERR(("ExecCmd::doexec: receive failed. errno %d\n", errno));
741
            LOGERR(("ExecCmd::doexec: receive failed. errno %d\n", errno));
512
  } else if (n > 0) {
742
        } else if (n > 0) {
513
      m_output->append(buf, n);
743
            m_output->append(buf, n);
514
      if (m_advise)
744
            if (m_advise) {
515
      m_advise->newData(n);
745
                m_advise->newData(n);
746
            }
516
  } // else n == 0, just return
747
        } // else n == 0, just return
517
  return n;
748
        return n;
518
    }
749
    }
519
private:
750
private:
520
    string        *m_output;
751
    string        *m_output;
521
    ExecCmdAdvise *m_advise;
752
    ExecCmdAdvise *m_advise;
522
};
753
};
523
754
524
755
525
int ExecCmd::doexec(const string &cmd, const vector<string>& args,
756
int ExecCmd::doexec(const string& cmd, const vector<string>& args,
526
          const string *input, string *output)
757
                    const string *input, string *output)
527
{
758
{
528
759
529
    if (startExec(cmd, args, input != 0, output != 0) < 0) {
760
    if (startExec(cmd, args, input != 0, output != 0) < 0) {
530
  return -1;
761
        return -1;
531
    }
762
    }
532
763
533
    // Cleanup in case we return early
764
    // Cleanup in case we return early
534
    ExecCmdRsrc e(this);
765
    ExecCmdRsrc e(this->m);
535
    SelectLoop myloop;
766
    SelectLoop myloop;
536
    int ret = 0;
767
    int ret = 0;
537
    if (input || output) {
768
    if (input || output) {
538
        // Setup output
769
        // Setup output
539
  if (output) {
770
        if (output) {
540
      NetconCli *oclicon = dynamic_cast<NetconCli *>(m_fromcmd.get());
771
            NetconCli *oclicon = m->m_fromcmd.get();
541
      if (!oclicon) {
772
            if (!oclicon) {
542
      LOGERR(("ExecCmd::doexec: no connection from command\n"));
773
                LOGERR(("ExecCmd::doexec: no connection from command\n"));
543
      return -1;
774
                return -1;
544
      }
775
            }
545
      oclicon->setcallback(make_shared<ExecReader>
776
            oclicon->setcallback(STD_SHARED_PTR<NetconWorker>
546
               (ExecReader(output, m_advise)));
777
                                 (new ExecReader(output, m->m_advise)));
547
      myloop.addselcon(m_fromcmd, Netcon::NETCONPOLL_READ);
778
            myloop.addselcon(m->m_fromcmd, Netcon::NETCONPOLL_READ);
548
      // Give up ownership 
779
            // Give up ownership
549
      m_fromcmd.reset();
780
            m->m_fromcmd.reset();
550
  } 
781
        }
551
        // Setup input
782
        // Setup input
552
  if (input) {
783
        if (input) {
553
      NetconCli *iclicon = dynamic_cast<NetconCli *>(m_tocmd.get());
784
            NetconCli *iclicon = m->m_tocmd.get();
554
      if (!iclicon) {
785
            if (!iclicon) {
555
      LOGERR(("ExecCmd::doexec: no connection from command\n"));
786
                LOGERR(("ExecCmd::doexec: no connection from command\n"));
556
      return -1;
787
                return -1;
557
      }
788
            }
558
      iclicon->setcallback(make_shared<ExecWriter>
789
            iclicon->setcallback(STD_SHARED_PTR<NetconWorker>
559
               (ExecWriter(input, m_provide)));
790
                                 (new ExecWriter(input, m->m_provide, m)));
560
      myloop.addselcon(m_tocmd, Netcon::NETCONPOLL_WRITE);
791
            myloop.addselcon(m->m_tocmd, Netcon::NETCONPOLL_WRITE);
561
      // Give up ownership 
792
            // Give up ownership
562
      m_tocmd.reset();
793
            m->m_tocmd.reset();
563
  }
794
        }
564
795
565
        // Do the actual reading/writing/waiting
796
        // Do the actual reading/writing/waiting
566
  myloop.setperiodichandler(0, 0, m_timeoutMs);
797
        myloop.setperiodichandler(0, 0, m->m_timeoutMs);
567
  while ((ret = myloop.doLoop()) > 0) {
798
        while ((ret = myloop.doLoop()) > 0) {
799
            LOGDEB(("ExecCmd::doexec: selectloop returned %d\n", ret));
800
            if (m->m_advise) {
801
                m->m_advise->newData(0);
802
            }
803
            if (m->m_killRequest) {
804
                LOGINFO(("ExecCmd::doexec: cancel request\n"));
805
                break;
806
            }
807
        }
568
      LOGDEB(("ExecCmd::doexec: selectloop returned %d\n", ret));
808
        LOGDEB0(("ExecCmd::doexec: selectloop returned %d\n", ret));
569
      if (m_advise)
570
      m_advise->newData(0);
571
      if (m_killRequest) {
572
      LOGINFO(("ExecCmd::doexec: cancel request\n"));
573
      break;
574
      }
575
  }
576
  LOGDEB0(("ExecCmd::doexec: selectloop returned %d\n", ret));
577
        // Check for interrupt request: we won't want to waitpid()
809
        // Check for interrupt request: we won't want to waitpid()
578
        if (m_advise)
810
        if (m->m_advise) {
579
            m_advise->newData(0);
811
            m->m_advise->newData(0);
812
        }
580
813
581
        // The netcons don't take ownership of the fds: we have to close them
814
        // The netcons don't take ownership of the fds: we have to close them
582
        // (have to do it before wait, this may be the signal the child is 
815
        // (have to do it before wait, this may be the signal the child is
583
        // waiting for exiting).
816
        // waiting for exiting).
584
        if (input) {
817
        if (input) {
585
            close(m_pipein[1]);
818
            close(m->m_pipein[1]);
586
            m_pipein[1] = -1;
819
            m->m_pipein[1] = -1;
587
        }
820
        }
588
        if (output) {
821
        if (output) {
589
            close(m_pipeout[0]);
822
            close(m->m_pipeout[0]);
590
            m_pipeout[0] = -1;
823
            m->m_pipeout[0] = -1;
591
        }
824
        }
592
    }
825
    }
593
826
594
    // Normal return: deactivate cleaner, wait() will do the cleanup
827
    // Normal return: deactivate cleaner, wait() will do the cleanup
595
    e.inactivate();
828
    e.inactivate();
596
829
597
    int ret1 = ExecCmd::wait();
830
    int ret1 = ExecCmd::wait();
598
    if (ret)
831
    if (ret) {
599
  return -1;
832
        return -1;
833
    }
600
    return ret1;
834
    return ret1;
601
}
835
}
602
836
603
int ExecCmd::send(const string& data)
837
int ExecCmd::send(const string& data)
604
{
838
{
605
    NetconCli *con = dynamic_cast<NetconCli *>(m_tocmd.get());
839
    NetconCli *con = m->m_tocmd.get();
606
    if (con == 0) {
840
    if (con == 0) {
607
  LOGERR(("ExecCmd::send: outpipe is closed\n"));
841
        LOGERR(("ExecCmd::send: outpipe is closed\n"));
608
  return -1;
842
        return -1;
609
    }
843
    }
610
    unsigned int nwritten = 0;
844
    unsigned int nwritten = 0;
611
    while (nwritten < data.length()) {
845
    while (nwritten < data.length()) {
612
  if (m_killRequest)
846
        if (m->m_killRequest) {
613
      break;
847
            break;
848
        }
614
  int n = con->send(data.c_str() + nwritten, data.length() - nwritten);
849
        int n = con->send(data.c_str() + nwritten, data.length() - nwritten);
615
  if (n < 0) {
850
        if (n < 0) {
616
      LOGERR(("ExecCmd::send: send failed\n"));
851
            LOGERR(("ExecCmd::send: send failed\n"));
617
      return -1;
852
            return -1;
618
  }
853
        }
619
  nwritten += n;
854
        nwritten += n;
620
    }
855
    }
621
    return nwritten;
856
    return nwritten;
622
}
857
}
623
858
624
int ExecCmd::receive(string& data, int cnt)
859
int ExecCmd::receive(string& data, int cnt)
625
{
860
{
626
    NetconCli *con = dynamic_cast<NetconCli *>(m_fromcmd.get());
861
    NetconCli *con = m->m_fromcmd.get();
627
    if (con == 0) {
862
    if (con == 0) {
628
  LOGERR(("ExecCmd::receive: inpipe is closed\n"));
863
        LOGERR(("ExecCmd::receive: inpipe is closed\n"));
629
  return -1;
864
        return -1;
630
    }
865
    }
631
    const int BS = 4096;
866
    const int BS = 4096;
632
    char buf[BS];
867
    char buf[BS];
633
    int ntot = 0;
868
    int ntot = 0;
634
    do {
869
    do {
...
...
646
        }
881
        }
647
    } while (cnt > 0 && ntot < cnt);
882
    } while (cnt > 0 && ntot < cnt);
648
    return ntot;
883
    return ntot;
649
}
884
}
650
885
651
int ExecCmd::getline(string& data, int timeo)
886
int ExecCmd::getline(string& data)
652
{
887
{
653
    NetconCli *con = dynamic_cast<NetconCli *>(m_fromcmd.get());
888
    NetconCli *con = m->m_fromcmd.get();
654
    if (con == 0) {
889
    if (con == 0) {
655
  LOGERR(("ExecCmd::receive: inpipe is closed\n"));
890
        LOGERR(("ExecCmd::receive: inpipe is closed\n"));
656
  return -1;
891
        return -1;
657
    }
892
    }
658
    const int BS = 1024;
893
    const int BS = 1024;
659
    char buf[BS];
894
    char buf[BS];
895
    int timeosecs = m->m_timeoutMs / 1000;
896
    if (timeosecs == 0) {
897
        timeosecs = 1;
898
    }
899
900
    // Note that we only go once through here, except in case of
901
    // timeout, which is why I think that the goto is more expressive
902
    // than a loop
903
again:
660
    int n = con->getline(buf, BS, timeo);
904
    int n = con->getline(buf, BS, timeosecs);
661
    if (n < 0) {
905
    if (n < 0) {
906
        if (con->timedout()) {
907
            LOGDEB(("ExecCmd::getline: timeout\n"));
908
            if (m->m_advise) {
909
                m->m_advise->newData(0);
910
            }
911
            goto again;
912
        }
662
  LOGERR(("ExecCmd::getline: error\n"));
913
        LOGERR(("ExecCmd::getline: error\n"));
663
    } else if (n > 0) {
914
    } else if (n > 0) {
664
  data.append(buf, n);
915
        data.append(buf, n);
665
    } else {
916
    } else {
666
  LOGDEB(("ExecCmd::getline: got 0\n"));
917
        LOGDEB(("ExecCmd::getline: got 0\n"));
667
    }
918
    }
668
    return n;
919
    return n;
669
}
920
}
670
921
922
class GetlineWatchdog : public ExecCmdAdvise {
923
public:
924
    GetlineWatchdog(int secs) : m_secs(secs), tstart(time(0)) {}
925
    void newData(int cnt) {
926
        if (time(0) - tstart >= m_secs) {
927
            throw std::runtime_error("getline timeout");
928
        }
929
    }
930
    int m_secs;
931
    time_t tstart;
932
};
933
934
int ExecCmd::getline(string& data, int timeosecs)
935
{
936
    GetlineWatchdog gwd(timeosecs);
937
    setAdvise(&gwd);
938
    try {
939
        return getline(data);
940
    } catch (...) {
941
        return -1;
942
    }
943
}
944
945
671
// Wait for command status and clean up all resources.
946
// Wait for command status and clean up all resources.
947
// We would like to avoid blocking here too, but there is no simple
948
// way to do this. The 2 possible approaches would be to:
949
//  - Use signals (alarm), waitpid() is interruptible. but signals and
950
//    threads... This would need a specialized thread, inter-thread comms etc.
951
//  - Use an intermediary process when starting the command. The
952
//    process forks a timer process, and the real command, then calls
953
//    a blocking waitpid on all at the end, and is guaranteed to get
954
//    at least the timer process status, thus yielding a select()
955
//    equivalent. This is bad too, because the timeout is on the whole
956
//    exec, not just the wait
957
// Just calling waitpid() with WNOHANG with a sleep() between tries
958
// does not work: the first waitpid() usually comes too early and
959
// reaps nothing, resulting in almost always one sleep() or more.
960
//
961
// So no timeout here. This has not been a problem in practise inside recoll.
962
// In case of need, using a semi-busy loop with short sleeps
963
// increasing from a few mS might work without creating too much
964
// overhead.
672
int ExecCmd::wait()
965
int ExecCmd::wait()
673
{
966
{
674
    ExecCmdRsrc e(this);
967
    ExecCmdRsrc e(this->m);
675
    int status = -1;
968
    int status = -1;
676
    if (!m_killRequest && m_pid > 0) {
969
    if (!m->m_killRequest && m->m_pid > 0) {
677
  if (waitpid(m_pid, &status, 0) < 0) {
970
        if (waitpid(m->m_pid, &status, 0) < 0) {
678
      LOGERR(("ExecCmd::waitpid: returned -1 errno %d\n", errno));
971
            LOGERR(("ExecCmd::waitpid: returned -1 errno %d\n", errno));
679
      status = -1;
972
            status = -1;
680
  }
973
        }
681
        LOGDEB(("ExecCmd::wait: got status 0x%x\n", status));
974
        LOGDEB(("ExecCmd::wait: got status 0x%x\n", status));
682
  m_pid = -1;
975
        m->m_pid = -1;
683
    }
976
    }
684
    // Let the ExecCmdRsrc cleanup
977
    // Let the ExecCmdRsrc cleanup, it will do the killing/waiting if needed
685
    return status;
978
    return status;
686
}
979
}
687
980
688
bool ExecCmd::maybereap(int *status)
981
bool ExecCmd::maybereap(int *status)
689
{
982
{
690
    ExecCmdRsrc e(this);
983
    ExecCmdRsrc e(this->m);
691
    *status = -1;
984
    *status = -1;
692
985
693
    if (m_pid <= 0) {
986
    if (m->m_pid <= 0) {
694
  // Already waited for ??
987
        // Already waited for ??
695
  return true;
988
        return true;
696
    }
989
    }
697
990
698
    pid_t pid = waitpid(m_pid, status, WNOHANG);
991
    pid_t pid = waitpid(m->m_pid, status, WNOHANG);
699
    if (pid < 0) {
992
    if (pid < 0) {
700
        LOGERR(("ExecCmd::maybereap: returned -1 errno %d\n", errno));
993
        LOGERR(("ExecCmd::maybereap: returned -1 errno %d\n", errno));
701
  m_pid = -1;
994
        m->m_pid = -1;
702
  return true;
995
        return true;
703
    } else if (pid == 0) {
996
    } else if (pid == 0) {
704
  LOGDEB1(("ExecCmd::maybereap: not exited yet\n"));
997
        LOGDEB1(("ExecCmd::maybereap: not exited yet\n"));
705
  e.inactivate();
998
        e.inactivate();
706
  return false;
999
        return false;
707
    } else {
1000
    } else {
708
        LOGDEB(("ExecCmd::maybereap: got status 0x%x\n", status));
1001
        LOGDEB(("ExecCmd::maybereap: got status 0x%x\n", status));
709
  m_pid = -1;
1002
        m->m_pid = -1;
710
  return true;
1003
        return true;
711
    }
1004
    }
712
}
1005
}
713
1006
714
// Static
1007
// Static
715
bool ExecCmd::backtick(const vector<string> cmd, string& out)
1008
bool ExecCmd::backtick(const vector<string> cmd, string& out)
...
...
729
}
1022
}
730
1023
731
void ReExec::init(int argc, char *args[])
1024
void ReExec::init(int argc, char *args[])
732
{
1025
{
733
    for (int i = 0; i < argc; i++) {
1026
    for (int i = 0; i < argc; i++) {
734
  m_argv.push_back(args[i]);
1027
        m_argv.push_back(args[i]);
735
    }
1028
    }
736
    m_cfd = open(".", 0);
1029
    m_cfd = open(".", 0);
737
    char *cd = getcwd(0, 0);
1030
    char *cd = getcwd(0, 0);
738
    if (cd) 
1031
    if (cd) {
739
  m_curdir = cd;
1032
        m_curdir = cd;
1033
    }
740
    free(cd);
1034
    free(cd);
741
}
1035
}
742
1036
743
void ReExec::insertArgs(const vector<string>& args, int idx)
1037
void ReExec::insertArgs(const vector<string>& args, int idx)
744
{
1038
{
745
    vector<string>::iterator it, cit;
1039
    vector<string>::iterator it, cit;
746
    unsigned int cmpoffset = (unsigned int)-1;
1040
    unsigned int cmpoffset = (unsigned int) - 1;
747
1041
748
    if (idx == -1 || string::size_type(idx) >= m_argv.size()) {
1042
    if (idx == -1 || string::size_type(idx) >= m_argv.size()) {
749
  it = m_argv.end();
1043
        it = m_argv.end();
750
  if (m_argv.size() >= args.size()) {
1044
        if (m_argv.size() >= args.size()) {
751
      cmpoffset = m_argv.size() - args.size();
1045
            cmpoffset = m_argv.size() - args.size();
752
  }
1046
        }
753
    } else {
1047
    } else {
754
  it = m_argv.begin() + idx;
1048
        it = m_argv.begin() + idx;
755
  if (idx + args.size() <= m_argv.size()) {
1049
        if (idx + args.size() <= m_argv.size()) {
756
      cmpoffset = idx;
1050
            cmpoffset = idx;
757
  }
1051
        }
758
    }
1052
    }
759
1053
760
    // Check that the option is not already there
1054
    // Check that the option is not already there
761
    if (cmpoffset != (unsigned int)-1) {
1055
    if (cmpoffset != (unsigned int) - 1) {
762
  bool allsame = true;
1056
        bool allsame = true;
763
  for (unsigned int i = 0; i < args.size(); i++) {
1057
        for (unsigned int i = 0; i < args.size(); i++) {
764
      if (m_argv[cmpoffset + i] != args[i]) {
1058
            if (m_argv[cmpoffset + i] != args[i]) {
765
      allsame = false;
1059
                allsame = false;
766
      break;
1060
                break;
767
      }
1061
            }
768
  }
1062
        }
769
  if (allsame)
1063
        if (allsame) {
770
      return;
1064
            return;
1065
        }
771
    }
1066
    }
772
1067
773
    m_argv.insert(it, args.begin(), args.end());
1068
    m_argv.insert(it, args.begin(), args.end());
774
}
1069
}
775
1070
776
void ReExec::removeArg(const string& arg)
1071
void ReExec::removeArg(const string& arg)
777
{
1072
{
778
    for (vector<string>::iterator it = m_argv.begin(); 
1073
    for (vector<string>::iterator it = m_argv.begin();
779
   it != m_argv.end(); it++) {
1074
            it != m_argv.end(); it++) {
780
  if (*it == arg)
1075
        if (*it == arg) {
781
      it = m_argv.erase(it);
1076
            it = m_argv.erase(it);
1077
        }
782
    }
1078
    }
783
}
1079
}
784
1080
785
// Reexecute myself, as close as possible to the initial exec
1081
// Reexecute myself, as close as possible to the initial exec
786
void ReExec::reexec()
1082
void ReExec::reexec()
787
{
1083
{
788
1084
789
#if 0
1085
#if 0
790
    char *cwd;
1086
    char *cwd;
791
    cwd = getcwd(0,0);
1087
    cwd = getcwd(0, 0);
792
    FILE *fp = stdout; //fopen("/tmp/exectrace", "w");
1088
    FILE *fp = stdout; //fopen("/tmp/exectrace", "w");
793
    if (fp) {
1089
    if (fp) {
794
  fprintf(fp, "reexec: pwd: [%s] args: ", cwd?cwd:"getcwd failed");
1090
        fprintf(fp, "reexec: pwd: [%s] args: ", cwd ? cwd : "getcwd failed");
795
  for (vector<string>::const_iterator it = m_argv.begin();
1091
        for (vector<string>::const_iterator it = m_argv.begin();
796
       it != m_argv.end(); it++) {
1092
                it != m_argv.end(); it++) {
797
      fprintf(fp, "[%s] ", it->c_str());
1093
            fprintf(fp, "[%s] ", it->c_str());
798
  }
1094
        }
799
  fprintf(fp, "\n");
1095
        fprintf(fp, "\n");
800
    }
1096
    }
801
#endif
1097
#endif
802
1098
803
    // Execute the atexit funcs
1099
    // Execute the atexit funcs
804
    while (!m_atexitfuncs.empty()) {
1100
    while (!m_atexitfuncs.empty()) {
805
  (m_atexitfuncs.top())();
1101
        (m_atexitfuncs.top())();
806
  m_atexitfuncs.pop();
1102
        m_atexitfuncs.pop();
807
    }
1103
    }
808
1104
809
    // Try to get back to the initial working directory
1105
    // Try to get back to the initial working directory
810
    if (m_cfd < 0 || fchdir(m_cfd) < 0) {
1106
    if (m_cfd < 0 || fchdir(m_cfd) < 0) {
811
  LOGINFO(("ReExec::reexec: fchdir failed, trying chdir\n"));
1107
        LOGINFO(("ReExec::reexec: fchdir failed, trying chdir\n"));
812
  if (!m_curdir.empty() && chdir(m_curdir.c_str())) {
1108
        if (!m_curdir.empty() && chdir(m_curdir.c_str())) {
813
      LOGERR(("ReExec::reexec: chdir failed\n"));
1109
            LOGERR(("ReExec::reexec: chdir failed\n"));
814
  }
1110
        }
815
    }
1111
    }
816
1112
817
    // Close all descriptors except 0,1,2
1113
    // Close all descriptors except 0,1,2
818
    libclf_closefrom(3);
1114
    libclf_closefrom(3);
819
1115
820
    // Allocate arg vector (1 more for final 0)
1116
    // Allocate arg vector (1 more for final 0)
821
    typedef const char *Ccharp;
1117
    typedef const char *Ccharp;
822
    Ccharp *argv;
1118
    Ccharp *argv;
823
    argv = (Ccharp *)malloc((m_argv.size()+1) * sizeof(char *));
1119
    argv = (Ccharp *)malloc((m_argv.size() + 1) * sizeof(char *));
824
    if (argv == 0) {
1120
    if (argv == 0) {
825
  LOGERR(("ExecCmd::doexec: malloc() failed. errno %d\n",  errno));
1121
        LOGERR(("ExecCmd::doexec: malloc() failed. errno %d\n", errno));
826
  return;
1122
        return;
827
    }
1123
    }
828
  
1124
829
    // Fill up argv
1125
    // Fill up argv
830
    int i = 0;
1126
    int i = 0;
831
    vector<string>::const_iterator it;
1127
    vector<string>::const_iterator it;
832
    for (it = m_argv.begin(); it != m_argv.end(); it++) {
1128
    for (it = m_argv.begin(); it != m_argv.end(); it++) {
833
  argv[i++] = it->c_str();
1129
        argv[i++] = it->c_str();
834
    }
1130
    }
835
    argv[i] = 0;
1131
    argv[i] = 0;
836
    execvp(m_argv[0].c_str(), (char *const*)argv);
1132
    execvp(m_argv[0].c_str(), (char *const*)argv);
837
}
1133
}
838
1134
839
1135
840
////////////////////////////////////////////////////////////////////
1136
////////////////////////////////////////////////////////////////////
841
#else // TEST
1137
#else // TEST
1138
842
#include <stdio.h>
1139
#include <stdio.h>
843
#include <stdlib.h>
1140
#include <stdlib.h>
844
#include <unistd.h>
1141
#include <unistd.h>
845
#include <string.h>
1142
#include <string.h>
1143
#include <signal.h>
846
1144
847
#include <string>
1145
#include <string>
848
#include <iostream>
1146
#include <iostream>
1147
#include <sstream>
849
#include <vector>
1148
#include <vector>
1149
1150
#include "debuglog.h"
1151
#include "execmd.h"
1152
#ifdef BUILDING_RECOLL
1153
#include "smallut.h"
1154
#include "cancelcheck.h"
1155
#endif
1156
850
using namespace std;
1157
using namespace std;
851
1158
852
#include "execmd.h"
1159
#ifdef BUILDING_RECOLL
1160
// Testing the rclexecm protocol outside of recoll. Here we use the
1161
// rcldoc.py filter, you can try with rclaudio too, adjust the file arg
1162
// accordingly
1163
bool exercise_mhexecm(const string& cmdstr, const string& mimetype,
1164
                      vector<string>& files)
1165
{
1166
    ExecCmd cmd;
1167
1168
    vector<string> myparams;
1169
1170
    if (cmd.startExec(cmdstr, myparams, 1, 1) < 0) {
1171
        cerr << "startExec " << cmdstr << " failed. Missing command?\n";
1172
        return false;
1173
    }
1174
1175
    for (vector<string>::const_iterator it = files.begin();
1176
            it != files.end(); it++) {
1177
        // Build request message
1178
        ostringstream obuf;
1179
        obuf << "Filename: " << (*it).length() << "\n" << (*it);
1180
        obuf << "Mimetype: " << mimetype.length() << "\n" << mimetype;
1181
        // Bogus parameter should be skipped by filter
1182
        obuf << "BogusParam: " << string("bogus").length() << "\n" << "bogus";
1183
        obuf << "\n";
1184
        cerr << "SENDING: [" << obuf.str() << "]\n";
1185
        // Send it
1186
        if (cmd.send(obuf.str()) < 0) {
1187
            // The real code calls zapchild here, but we don't need it as
1188
            // this will be handled by ~ExecCmd
1189
            //cmd.zapChild();
1190
            cerr << "send error\n";
1191
            return false;
1192
        }
1193
1194
        // Read answer
1195
        for (int loop = 0;; loop++) {
1196
            string name, data;
1197
1198
            // Code from mh_execm.cpp: readDataElement
1199
            string ibuf;
1200
            // Read name and length
1201
            if (cmd.getline(ibuf) <= 0) {
1202
                cerr << "getline error\n";
1203
                return false;
1204
            }
1205
            // Empty line (end of message)
1206
            if (!ibuf.compare("\n")) {
1207
                cerr << "Got empty line\n";
1208
                name.clear();
1209
                break;
1210
            }
1211
1212
            // Filters will sometimes abort before entering the real
1213
            // protocol, ie if a module can't be loaded. Check the
1214
            // special filter error first word:
1215
            if (ibuf.find("RECFILTERROR ") == 0) {
1216
                cerr << "Got RECFILTERROR\n";
1217
                return false;
1218
            }
1219
1220
            // We're expecting something like Name: len\n
1221
            vector<string> tokens;
1222
            stringToTokens(ibuf, tokens);
1223
            if (tokens.size() != 2) {
1224
                cerr << "bad line in filter output: [" << ibuf << "]\n";
1225
                return false;
1226
            }
1227
            vector<string>::iterator it = tokens.begin();
1228
            name = *it++;
1229
            string& slen = *it;
1230
            int len;
1231
            if (sscanf(slen.c_str(), "%d", &len) != 1) {
1232
                cerr << "bad line in filter output (no len): [" <<
1233
                     ibuf << "]\n";
1234
                return false;
1235
            }
1236
            // Read element data
1237
            data.erase();
1238
            if (len > 0 && cmd.receive(data, len) != len) {
1239
                cerr << "MHExecMultiple: expected " << len <<
1240
                     " bytes of data, got " << data.length() << endl;
1241
                return false;
1242
            }
1243
1244
            // Empty element: end of message
1245
            if (name.empty()) {
1246
                break;
1247
            }
1248
            cerr << "Got name: [" << name << "] data [" << data << "]\n";
1249
        }
1250
    }
1251
    return true;
1252
}
1253
#endif
1254
1255
static char *thisprog;
1256
static char usage [] =
1257
    "trexecmd [-c -r -i -o] cmd [arg1 arg2 ...]\n"
1258
    "   -c : test cancellation (ie: trexecmd -c sleep 1000)\n"
1259
    "   -r : run reexec. Must be separate option.\n"
1260
    "   -i : command takes input\n"
1261
    "   -o : command produces output\n"
1262
    "    If -i is set, we send /etc/group contents to whatever command is run\n"
1263
    "    If -o is set, we print whatever comes out\n"
1264
    "trexecmd -m <filter> <mimetype> <file> [file ...]: test execm:\n"
1265
    "     <filter> should be the path to an execm filter\n"
1266
    "     <mimetype> the type of the file parameters\n"
1267
    "trexecmd -w cmd : do the 'which' thing\n"
1268
    "trexecmd -l cmd test getline\n"
1269
    ;
1270
1271
static void Usage(void)
1272
{
1273
    fprintf(stderr, "%s: usage:\n%s", thisprog, usage);
1274
    exit(1);
1275
}
853
1276
854
static int     op_flags;
1277
static int     op_flags;
855
#define OPT_MOINS 0x1
1278
#define OPT_MOINS 0x1
856
#define OPT_b   0x4 
1279
#define OPT_i     0x4
857
#define OPT_w     0x8
1280
#define OPT_w     0x8
858
#define OPT_c     0x10
1281
#define OPT_c     0x10
859
#define OPT_r     0x20
1282
#define OPT_r     0x20
1283
#define OPT_m     0x40
1284
#define OPT_o     0x80
1285
#define OPT_l     0x100
860
1286
861
const char *data = "Une ligne de donnees\n";
1287
// Data sink for data coming out of the command. We also use it to set
862
class CancelExcept {};
1288
// a cancellation after a moment.
863
class MEAdv : public ExecCmdAdvise {
1289
class MEAdv : public ExecCmdAdvise {
864
public:
1290
public:
865
    ExecCmd *cmd;
866
    void newData(int cnt) {
1291
    void newData(int cnt) {
867
  if (op_flags & OPT_c) {
1292
        if (op_flags & OPT_c) {
1293
#ifdef BUILDING_RECOLL
868
      static int  callcnt;
1294
            static int  callcnt;
869
      if (callcnt++ == 3) {
1295
            if (callcnt++ == 10) {
870
      throw CancelExcept();
1296
                // Just sets the cancellation flag
871
      }
1297
                CancelCheck::instance().setCancel();
872
  }
1298
                // Would be called from somewhere else and throws an
1299
                // exception. We call it here for simplicity
1300
                CancelCheck::instance().checkCancel();
1301
            }
1302
#endif
1303
        }
873
  cerr << "newData(" << cnt << ")" << endl;
1304
        cerr << "newData(" << cnt << ")" << endl;
874
  //  CancelCheck::instance().setCancel();
875
  //  CancelCheck::instance().checkCancel();
876
  //  cmd->setCancel();
877
    }
1305
    }
878
};
1306
};
879
1307
1308
// Data provider, used if the -i flag is set
880
class MEPv : public ExecCmdProvide {
1309
class MEPv : public ExecCmdProvide {
881
public:
1310
public:
882
    FILE *m_fp;
1311
    FILE *m_fp;
883
    string *m_input;
1312
    string *m_input;
884
    MEPv(string *i) 
1313
    MEPv(string *i)
885
  : m_input(i)
1314
        : m_input(i) {
886
    {
887
  m_fp = fopen("/etc/group", "r");
1315
        m_fp = fopen("/etc/group", "r");
888
    }
1316
    }
889
    ~MEPv() {
1317
    ~MEPv() {
890
  if (m_fp)
1318
        if (m_fp) {
891
      fclose(m_fp);
1319
            fclose(m_fp);
1320
        }
892
    }
1321
    }
893
    void newData() {
1322
    void newData() {
894
  char line[1024];
1323
        char line[1024];
895
  if (m_fp && fgets(line, 1024, m_fp)) {
1324
        if (m_fp && fgets(line, 1024, m_fp)) {
896
      m_input->assign((const char *)line);
1325
            m_input->assign((const char *)line);
897
  } else {
1326
        } else {
898
      m_input->erase();
1327
            m_input->erase();
899
  }
1328
        }
900
    }
1329
    }
901
};
1330
};
902
1331
903
1332
904
static char *thisprog;
905
static char usage [] =
906
"trexecmd [-c|-r] cmd [arg1 arg2 ...]\n" 
907
" -c : test cancellation (ie: trexecmd -c sleep 1000)\n"
908
" -r : test reexec\n"
909
"trexecmd -w cmd : do the which thing\n"
910
;
911
static void Usage(void)
912
{
913
    fprintf(stderr, "%s: usage:\n%s", thisprog, usage);
914
    exit(1);
915
}
916
1333
917
ReExec reexec;
1334
ReExec reexec;
918
919
int main(int argc, char *argv[])
1335
int main(int argc, char *argv[])
920
{
1336
{
921
    reexec.init(argc, argv);
1337
    reexec.init(argc, argv);
922
1338
923
    if (0) {
1339
    if (0) {
1340
        // Disabled: For testing reexec arg handling
924
  vector<string> newargs;
1341
        vector<string> newargs;
925
  newargs.push_back("newarg");
1342
        newargs.push_back("newarg");
926
  newargs.push_back("newarg1");
1343
        newargs.push_back("newarg1");
927
  newargs.push_back("newarg2");
1344
        newargs.push_back("newarg2");
928
  newargs.push_back("newarg3");
1345
        newargs.push_back("newarg3");
929
  newargs.push_back("newarg4");
1346
        newargs.push_back("newarg4");
930
  reexec.insertArgs(newargs, 2);
1347
        reexec.insertArgs(newargs, 2);
931
    }
1348
    }
932
1349
933
    thisprog = argv[0];
1350
    thisprog = argv[0];
934
    argc--; argv++;
1351
    argc--;
1352
    argv++;
935
1353
936
    while (argc > 0 && **argv == '-') {
1354
    while (argc > 0 && **argv == '-') {
937
  (*argv)++;
1355
        (*argv)++;
938
  if (!(**argv))
1356
        if (!(**argv))
939
      /* Cas du "adb - core" */
1357
            /* Cas du "adb - core" */
940
      Usage();
1358
        {
1359
            Usage();
1360
        }
941
  while (**argv)
1361
        while (**argv)
942
      switch (*(*argv)++) {
1362
            switch (*(*argv)++) {
943
      case 'c':   op_flags |= OPT_c; break;
1363
            case 'c':
944
      case 'r':   op_flags |= OPT_r; break;
1364
                op_flags |= OPT_c;
945
      case 'w':   op_flags |= OPT_w; break;
1365
                break;
946
      default: Usage();   break;
1366
            case 'r':
1367
                op_flags |= OPT_r;
1368
                break;
1369
            case 'w':
1370
                op_flags |= OPT_w;
1371
                break;
1372
#ifdef BUILDING_RECOLL
1373
            case 'm':
1374
                op_flags |= OPT_m;
1375
                break;
1376
#endif
1377
            case 'i':
1378
                op_flags |= OPT_i;
1379
                break;
1380
            case 'l':
1381
                op_flags |= OPT_l;
1382
                break;
1383
            case 'o':
1384
                op_flags |= OPT_o;
1385
                break;
1386
            default:
1387
                Usage();
1388
                break;
1389
            }
1390
        argc--;
1391
        argv++;
947
      }
1392
    }
948
        argc--; argv++;
949
    }
950
1393
951
    if (argc < 1)
1394
    if (argc < 1) {
952
  Usage();
1395
        Usage();
1396
    }
953
1397
954
    string cmd = *argv++; argc--;
1398
    string arg1 = *argv++;
1399
    argc--;
955
    vector<string> l;
1400
    vector<string> l;
956
    while (argc > 0) {
1401
    while (argc > 0) {
957
  l.push_back(*argv++); argc--;
1402
        l.push_back(*argv++);
1403
        argc--;
958
    }
1404
    }
1405
1406
#ifdef BUILDING_RECOLL
959
    //DebugLog::getdbl()->setloglevel(DEBDEB1);
1407
    DebugLog::getdbl()->setloglevel(DEBDEB1);
960
    //DebugLog::setfilename("stderr");
1408
    DebugLog::setfilename("stderr");
1409
#endif
961
    signal(SIGPIPE, SIG_IGN);
1410
    signal(SIGPIPE, SIG_IGN);
962
1411
963
    if (op_flags & OPT_r) {
1412
    if (op_flags & OPT_r) {
964
  chdir("/");
1413
        // Test reexec. Normally only once, next time we fall through
1414
        // because we remove the -r option (only works if it was isolated, not like -rc
1415
        chdir("/");
965
        argv[0] = strdup("");
1416
        argv[0] = strdup("");
966
  sleep(1);
1417
        sleep(1);
1418
        cerr << "Calling reexec\n";
1419
        // We remove the -r arg from list, otherwise we are going to
1420
        // loop (which you can try by commenting out the following
1421
        // line)
1422
        reexec.removeArg("-r");
967
        reexec.reexec();
1423
        reexec.reexec();
968
    }
1424
    }
969
1425
970
    if (op_flags & OPT_w) {
1426
    if (op_flags & OPT_w) {
971
  string path;
1427
        // Test "which" method
1428
        string path;
972
  if (ExecCmd::which(cmd, path)) {
1429
        if (ExecCmd::which(arg1, path)) {
973
      cout << path << endl;
1430
            cout << path << endl;
974
      exit(0);
1431
            return 0;
975
  } 
976
  exit(1);
977
    }
1432
        }
1433
        return 1;
1434
#ifdef BUILDING_RECOLL
1435
    } else if (op_flags & OPT_m) {
1436
        if (l.size() < 2) {
1437
            Usage();
1438
        }
1439
        string mimetype = l[0];
1440
        l.erase(l.begin());
1441
        return exercise_mhexecm(arg1, mimetype, l) ? 0 : 1;
1442
#endif
1443
    } else if (op_flags & OPT_l) {
978
    ExecCmd mexec;
1444
        ExecCmd mexec;
1445
1446
        if (mexec.startExec(arg1, l, false, true) < 0) {
1447
            cerr << "Startexec failed\n";
1448
            exit(1);
1449
        }
1450
        string output;
1451
        int ret = mexec.getline(output, 2);
1452
        cerr << "Got ret " << ret << " output " << output << endl;
1453
        cerr << "Waiting\n";
1454
        int status = mexec.wait();
1455
        cerr << "Got status " << status << endl;
1456
        exit(status);
1457
    } else {
1458
        // Default: execute command line arguments
1459
        ExecCmd mexec;
1460
1461
        // Set callback to be called whenever there is new data
1462
        // available and at a periodic interval, to check for
1463
        // cancellation
979
    MEAdv adv;
1464
        MEAdv adv;
980
    adv.cmd = &mexec;
981
    mexec.setAdvise(&adv);
1465
        mexec.setAdvise(&adv);
982
    mexec.setTimeout(5);
1466
        mexec.setTimeout(5);
1467
1468
        // Stderr output goes there
983
    mexec.setStderr("/tmp/trexecStderr");
1469
        mexec.setStderr("/tmp/trexecStderr");
1470
1471
        // A few environment variables. Check with trexecmd env
984
    mexec.putenv("TESTVARIABLE1=TESTVALUE1");
1472
        mexec.putenv("TESTVARIABLE1=TESTVALUE1");
985
    mexec.putenv("TESTVARIABLE2=TESTVALUE2");
1473
        mexec.putenv("TESTVARIABLE2=TESTVALUE2");
986
    mexec.putenv("TESTVARIABLE3=TESTVALUE3");
1474
        mexec.putenv("TESTVARIABLE3=TESTVALUE3");
987
1475
988
    string input, output;
1476
        string input, output;
989
    //    input = data;
990
    string *ip = 0;
991
    ip = &input;
992
993
    MEPv  pv(&input);
1477
        MEPv  pv(&input);
1478
1479
        string *ip = 0;
1480
        if (op_flags  & OPT_i) {
1481
            ip = &input;
994
    mexec.setProvide(&pv);
1482
            mexec.setProvide(&pv);
1483
        }
1484
        string *op = 0;
1485
        if (op_flags & OPT_o) {
1486
            op = &output;
1487
        }
995
1488
996
    int status = -1;
1489
        int status = -1;
997
    try {
1490
        try {
998
  status = mexec.doexec(cmd, l, ip, &output);
1491
            status = mexec.doexec(arg1, l, ip, op);
999
    } catch (CancelExcept) {
1492
        } catch (...) {
1000
  cerr << "CANCELLED" << endl;
1493
            cerr << "CANCELLED" << endl;
1001
    }
1494
        }
1002
1495
1003
    fprintf(stderr, "Status: 0x%x\n", status);
1496
        fprintf(stderr, "Status: 0x%x\n", status);
1497
        if (op_flags & OPT_o) {
1004
    cout << output;
1498
            cout << output;
1499
        }
1005
    exit (status >> 8);
1500
        exit(status >> 8);
1501
    }
1006
}
1502
}
1007
#endif // TEST
1503
#endif // TEST