Switch to unified view

a/src/utils/execmd.cpp b/src/utils/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 RECOLL_DATADIR
18
#ifdef BUILDING_RECOLL
19
#include "autoconfig.h"
19
#include "autoconfig.h"
20
#else
20
#else
21
#include "config.h"
21
#include "config.h"
22
#endif
22
#endif
23
23
...
...
55
55
56
using namespace std;
56
using namespace std;
57
57
58
extern char **environ;
58
extern char **environ;
59
59
60
bool ExecCmd::o_useVfork = false;
60
#ifdef BUILDING_RECOLL
61
62
#ifdef RECOLL_DATADIR
63
#include "debuglog.h"
61
#include "debuglog.h"
64
#include "smallut.h"
62
#include "smallut.h"
65
63
66
#else
64
#else
67
// If compiling outside of recoll, make the file as standalone as reasonable.
65
// If compiling outside of recoll, make the file as standalone as reasonable.
...
...
110
        tokens.push_back(str.substr(startPos, pos - startPos));
108
        tokens.push_back(str.substr(startPos, pos - startPos));
111
        startPos = ++pos;
109
        startPos = ++pos;
112
    }
110
    }
113
    }
111
    }
114
}
112
}
115
#endif // RECOLL_DATADIR
113
#endif // BUILDING_RECOLL
114
115
class ExecCmd::Internal {
116
public:
117
    Internal()
118
    : m_advise(0), m_provide(0), m_timeoutMs(1000), 
119
      m_rlimit_as_mbytes(0) {
120
    }
121
122
    static bool      o_useVfork;
123
124
    std::vector<std::string>   m_env;
125
    ExecCmdAdvise   *m_advise;
126
    ExecCmdProvide  *m_provide;
127
    bool             m_killRequest;
128
    int              m_timeoutMs;
129
    int              m_rlimit_as_mbytes;
130
    string           m_stderrFile;
131
    // Pipe for data going to the command
132
    int              m_pipein[2];
133
    STD_SHARED_PTR<NetconCli> m_tocmd;
134
    // Pipe for data coming out
135
    int              m_pipeout[2];
136
    STD_SHARED_PTR<NetconCli> m_fromcmd;
137
    // Subprocess id
138
    pid_t            m_pid;
139
    // Saved sigmask
140
    sigset_t         m_blkcld;
141
142
    // Reset internal state indicators. Any resources should have been
143
    // previously freed
144
    void reset() {
145
  m_killRequest = false;
146
  m_pipein[0] = m_pipein[1] = m_pipeout[0] = m_pipeout[1] = -1;
147
  m_pid = -1;
148
  sigemptyset(&m_blkcld);
149
    }
150
    // Child process code
151
    inline void dochild(const std::string &cmd, const char **argv, 
152
          const char **envv, bool has_input, bool has_output);
153
};
154
bool ExecCmd::Internal::o_useVfork = false;
155
156
ExecCmd::ExecCmd()
157
{
158
    m = new Internal();
159
    if (m)
160
        m->reset();
161
}
162
void ExecCmd::setAdvise(ExecCmdAdvise *adv) 
163
{
164
    m->m_advise = adv;
165
}
166
void ExecCmd::setProvide(ExecCmdProvide *p) 
167
{
168
    m->m_provide = p;
169
}
170
void ExecCmd::setTimeout(int mS) 
171
{
172
    if (mS > 30) 
173
        m->m_timeoutMs = mS;
174
}
175
void ExecCmd::setStderr(const std::string &stderrFile) 
176
{
177
    m->m_stderrFile = stderrFile;
178
}
179
pid_t ExecCmd::getChildPid() 
180
{
181
    return m->m_pid;
182
}
183
void ExecCmd::setKill() 
184
{
185
    m->m_killRequest = true;
186
}
187
void ExecCmd::zapChild() 
188
{
189
    setKill(); 
190
    (void)wait();
191
}
192
116
193
117
/* From FreeBSD's which command */
194
/* From FreeBSD's which command */
118
static bool exec_is_there(const char *candidate)
195
static bool exec_is_there(const char *candidate)
119
{
196
{
120
    struct stat fin;
197
    struct stat fin;
...
...
172
    // dynamic linker can sometimes deadlock if execve() is resolved
249
    // dynamic linker can sometimes deadlock if execve() is resolved
173
    // inside the vfork/exec window. Make sure it's done now. If "/" is
250
    // inside the vfork/exec window. Make sure it's done now. If "/" is
174
    // an executable file, we have a problem.
251
    // an executable file, we have a problem.
175
    const char *argv[] = {"/", 0};
252
    const char *argv[] = {"/", 0};
176
    execve("/", (char *const *)argv, environ);
253
    execve("/", (char *const *)argv, environ);
177
    o_useVfork  = on;
254
    Internal::o_useVfork  = on;
178
}
255
}
179
256
180
void ExecCmd::putenv(const string &ea)
257
void ExecCmd::putenv(const string &ea)
181
{
258
{
182
    m_env.push_back(ea);
259
    m->m_env.push_back(ea);
183
}
260
}
184
261
185
void  ExecCmd::putenv(const string &name, const string& value)
262
void  ExecCmd::putenv(const string &name, const string& value)
186
{
263
{
187
    string ea = name + "=" + value;
264
    string ea = name + "=" + value;
...
...
199
/** A resource manager to ensure that execcmd cleans up if an exception is 
276
/** A resource manager to ensure that execcmd cleans up if an exception is 
200
 *  raised in the callback, or at different places on errors occurring
277
 *  raised in the callback, or at different places on errors occurring
201
 *  during method executions */
278
 *  during method executions */
202
class ExecCmdRsrc {
279
class ExecCmdRsrc {
203
public:
280
public:
204
    ExecCmdRsrc(ExecCmd *parent) : m_parent(parent), m_active(true) {}
281
    ExecCmdRsrc(ExecCmd::Internal *parent) 
205
    void inactivate() {m_active = false;}
282
        : m_parent(parent), m_active(true) {
283
    }
284
    void inactivate() {
285
        m_active = false;
286
    }
206
    ~ExecCmdRsrc() {
287
    ~ExecCmdRsrc() {
207
    if (!m_active || !m_parent)
288
    if (!m_active || !m_parent)
208
        return;
289
        return;
209
    LOGDEB1(("~ExecCmdRsrc: working. mypid: %d\n", (int)getpid()));
290
    LOGDEB1(("~ExecCmdRsrc: working. mypid: %d\n", (int)getpid()));
210
291
...
...
248
    m_parent->m_fromcmd.reset();
329
    m_parent->m_fromcmd.reset();
249
    pthread_sigmask(SIG_UNBLOCK, &m_parent->m_blkcld, 0);
330
    pthread_sigmask(SIG_UNBLOCK, &m_parent->m_blkcld, 0);
250
    m_parent->reset();
331
    m_parent->reset();
251
    }
332
    }
252
private:
333
private:
253
    ExecCmd *m_parent;
334
    ExecCmd::Internal *m_parent;
254
    bool    m_active;
335
    bool    m_active;
255
};
336
};
256
337
257
ExecCmd::~ExecCmd()
338
ExecCmd::~ExecCmd()
258
{
339
{
259
    ExecCmdRsrc(this);
340
    ExecCmdRsrc(this->m);
341
    if (m)
342
        delete m;
260
}
343
}
261
344
262
// In child process. Set up pipes and exec command. 
345
// In child process. Set up pipes and exec command. 
263
// This must not return. _exit() on error.
346
// This must not return. _exit() on error.
264
// *** This can be called after a vfork, so no modification of the
347
// *** This can be called after a vfork, so no modification of the
...
...
270
// father process, so that only absolutely exceptional conditions, 
353
// father process, so that only absolutely exceptional conditions, 
271
// should be logged, for debugging and post-mortem purposes
354
// should be logged, for debugging and post-mortem purposes
272
// If one of the calls block, the problem manifests itself by 20mn
355
// If one of the calls block, the problem manifests itself by 20mn
273
// (filter timeout) of looping on "ExecCmd::doexec: selectloop
356
// (filter timeout) of looping on "ExecCmd::doexec: selectloop
274
// returned 1', because the father is waiting on the read descriptor
357
// returned 1', because the father is waiting on the read descriptor
275
inline void ExecCmd::dochild(const string &cmd, const char **argv,
358
inline void ExecCmd::Internal::dochild(const string &cmd, const char **argv,
276
               const char **envv,
359
                                       const char **envv,
277
               bool has_input, bool has_output)
360
                                       bool has_input, bool has_output)
278
{
361
{
279
    // Start our own process group
362
    // Start our own process group
280
    if (setpgid(0, getpid())) {
363
    if (setpgid(0, getpid())) {
281
    LOGINFO(("ExecCmd::DOCHILD: setpgid(0, %d) failed: errno %d\n",
364
    LOGINFO(("ExecCmd::DOCHILD: setpgid(0, %d) failed: errno %d\n",
282
         getpid(), errno));
365
         getpid(), errno));
...
...
382
    _exit(127);
465
    _exit(127);
383
}
466
}
384
467
385
void ExecCmd::setrlimit_as(int mbytes)
468
void ExecCmd::setrlimit_as(int mbytes)
386
{
469
{
387
    m_rlimit_as_mbytes = mbytes;
470
    m->m_rlimit_as_mbytes = mbytes;
388
}
471
}
389
472
390
int ExecCmd::startExec(const string &cmd, const vector<string>& args,
473
int ExecCmd::startExec(const string &cmd, const vector<string>& args,
391
               bool has_input, bool has_output)
474
               bool has_input, bool has_output)
392
{
475
{
...
...
399
    LOGDEB(("ExecCmd::startExec: (%d|%d) %s\n", 
482
    LOGDEB(("ExecCmd::startExec: (%d|%d) %s\n", 
400
        has_input, has_output, command.c_str()));
483
        has_input, has_output, command.c_str()));
401
    }
484
    }
402
485
403
    // The resource manager ensures resources are freed if we return early
486
    // The resource manager ensures resources are freed if we return early
404
    ExecCmdRsrc e(this);
487
    ExecCmdRsrc e(this->m);
405
488
406
    if (has_input && pipe(m_pipein) < 0) {
489
    if (has_input && pipe(m->m_pipein) < 0) {
407
    LOGERR(("ExecCmd::startExec: pipe(2) failed. errno %d\n", errno));
490
    LOGERR(("ExecCmd::startExec: pipe(2) failed. errno %d\n", errno));
408
    return -1;
491
    return -1;
409
    }
492
    }
410
    if (has_output && pipe(m_pipeout) < 0) {
493
    if (has_output && pipe(m->m_pipeout) < 0) {
411
    LOGERR(("ExecCmd::startExec: pipe(2) failed. errno %d\n", errno));
494
    LOGERR(("ExecCmd::startExec: pipe(2) failed. errno %d\n", errno));
412
    return -1;
495
    return -1;
413
    }
496
    }
414
497
415
498
...
...
439
    Ccharp *envv;
522
    Ccharp *envv;
440
    int envsize;
523
    int envsize;
441
    for (envsize = 0; ; envsize++) 
524
    for (envsize = 0; ; envsize++) 
442
    if (environ[envsize] == 0)
525
    if (environ[envsize] == 0)
443
        break;
526
        break;
444
    envv = (Ccharp *)malloc((envsize + m_env.size() + 2) * sizeof(char *));
527
    envv = (Ccharp *)malloc((envsize + m->m_env.size() + 2) * sizeof(char *));
445
    if (envv == 0) {
528
    if (envv == 0) {
446
    LOGERR(("ExecCmd::doexec: malloc() failed. errno %d\n", errno));
529
    LOGERR(("ExecCmd::doexec: malloc() failed. errno %d\n", errno));
447
        free(argv);
530
        free(argv);
448
        return -1;
531
        return -1;
449
    }
532
    }
450
    int eidx;
533
    int eidx;
451
    for (eidx = 0; eidx < envsize; eidx++)
534
    for (eidx = 0; eidx < envsize; eidx++)
452
    envv[eidx] = environ[eidx];
535
    envv[eidx] = environ[eidx];
453
    for (vector<string>::const_iterator it = m_env.begin(); 
536
    for (vector<string>::const_iterator it = m->m_env.begin(); 
454
     it != m_env.end(); it++) {
537
     it != m->m_env.end(); it++) {
455
    envv[eidx++] = it->c_str();
538
    envv[eidx++] = it->c_str();
456
    }
539
    }
457
    envv[eidx] = 0;
540
    envv[eidx] = 0;
458
541
459
    // As we are going to use execve, not execvp, do the PATH thing.
542
    // As we are going to use execve, not execvp, do the PATH thing.
...
...
493
576
494
        posix_spawn_file_actions_t facts;
577
        posix_spawn_file_actions_t facts;
495
        posix_spawn_file_actions_init(&facts);
578
        posix_spawn_file_actions_init(&facts);
496
579
497
        if (has_input) {
580
        if (has_input) {
498
            posix_spawn_file_actions_addclose(&facts, m_pipein[1]);
581
            posix_spawn_file_actions_addclose(&facts, m->m_pipein[1]);
499
            if (m_pipein[0] != 0) {
582
            if (m->m_pipein[0] != 0) {
500
                posix_spawn_file_actions_adddup2(&facts, m_pipein[0], 0);
583
                posix_spawn_file_actions_adddup2(&facts, m->m_pipein[0], 0);
501
                posix_spawn_file_actions_addclose(&facts, m_pipein[0]);
584
                posix_spawn_file_actions_addclose(&facts, m->m_pipein[0]);
502
            }
585
            }
503
        }
586
        }
504
        if (has_output) {
587
        if (has_output) {
505
            posix_spawn_file_actions_addclose(&facts, m_pipeout[0]);
588
            posix_spawn_file_actions_addclose(&facts, m->m_pipeout[0]);
506
            if (m_pipeout[1] != 1) {
589
            if (m->m_pipeout[1] != 1) {
507
                posix_spawn_file_actions_adddup2(&facts, m_pipeout[1], 1);
590
                posix_spawn_file_actions_adddup2(&facts, m->m_pipeout[1], 1);
508
                posix_spawn_file_actions_addclose(&facts, m_pipeout[1]);
591
                posix_spawn_file_actions_addclose(&facts, m->m_pipeout[1]);
509
            }
592
            }
510
        }
593
        }
511
594
512
        // Do we need to redirect stderr ?
595
        // Do we need to redirect stderr ?
513
        if (!m_stderrFile.empty()) {
596
        if (!m->m_stderrFile.empty()) {
514
            int oflags = O_WRONLY|O_CREAT;
597
            int oflags = O_WRONLY|O_CREAT;
515
#ifdef O_APPEND
598
#ifdef O_APPEND
516
            oflags |= O_APPEND;
599
            oflags |= O_APPEND;
517
#endif
600
#endif
518
            posix_spawn_file_actions_addopen(&facts, 2, m_stderrFile.c_str(), 
601
            posix_spawn_file_actions_addopen(&facts, 2, m->m_stderrFile.c_str(), 
519
                                             oflags, 0600);
602
                                             oflags, 0600);
520
        }
603
        }
521
        LOGDEB1(("using SPAWN\n"));
604
        LOGDEB1(("using SPAWN\n"));
522
605
523
        // posix_spawn() does not have any standard way to ask for
606
        // posix_spawn() does not have any standard way to ask for
...
...
525
        // but let's just add all fds
608
        // but let's just add all fds
526
        for (int i = 3; i < libclf_maxfd(); i++) {
609
        for (int i = 3; i < libclf_maxfd(); i++) {
527
            posix_spawn_file_actions_addclose(&facts, i);
610
            posix_spawn_file_actions_addclose(&facts, i);
528
        }
611
        }
529
612
530
        int ret = posix_spawn(&m_pid, exe.c_str(), &facts, &attrs, 
613
        int ret = posix_spawn(&m->m_pid, exe.c_str(), &facts, &attrs, 
531
                              (char *const *)argv, (char *const *)envv);
614
                              (char *const *)argv, (char *const *)envv);
532
        posix_spawnattr_destroy(&attrs);
615
        posix_spawnattr_destroy(&attrs);
533
        posix_spawn_file_actions_destroy(&facts);
616
        posix_spawn_file_actions_destroy(&facts);
534
        if (ret) {
617
        if (ret) {
535
            LOGERR(("ExecCmd::startExec: posix_spawn() failed. errno %d\n", 
618
            LOGERR(("ExecCmd::startExec: posix_spawn() failed. errno %d\n", 
...
...
537
            return -1;
620
            return -1;
538
        }
621
        }
539
    }
622
    }
540
623
541
#else
624
#else
542
    if (o_useVfork) {
625
    if (Internal::o_useVfork) {
543
        LOGDEB1(("using VFORK\n"));
626
        LOGDEB1(("using VFORK\n"));
544
    m_pid = vfork();
627
    m->m_pid = vfork();
545
    } else {
628
    } else {
546
        LOGDEB1(("using FORK\n"));
629
        LOGDEB1(("using FORK\n"));
547
    m_pid = fork();
630
    m->m_pid = fork();
548
    }
631
    }
549
    if (m_pid < 0) {
632
    if (m->m_pid < 0) {
550
    LOGERR(("ExecCmd::startExec: fork(2) failed. errno %d\n", errno));
633
    LOGERR(("ExecCmd::startExec: fork(2) failed. errno %d\n", errno));
551
    return -1;
634
    return -1;
552
    }
635
    }
553
    if (m_pid == 0) {
636
    if (m->m_pid == 0) {
554
    // e.inactivate() is not needed. As we do not return, the call
637
    // e.inactivate() is not needed. As we do not return, the call
555
    // stack won't be unwound and destructors of local objects
638
    // stack won't be unwound and destructors of local objects
556
    // won't be called.
639
    // won't be called.
557
    dochild(exe, argv, envv, has_input, has_output);
640
    m->dochild(exe, argv, envv, has_input, has_output);
558
    // dochild does not return. Just in case...
641
    // dochild does not return. Just in case...
559
    _exit(1);
642
    _exit(1);
560
    }
643
    }
561
#endif
644
#endif
562
645
...
...
568
    free(envv);
651
    free(envv);
569
///////////////////
652
///////////////////
570
653
571
    // Set the process group for the child. This is also done in the
654
    // Set the process group for the child. This is also done in the
572
    // child process see wikipedia(Process_group)
655
    // child process see wikipedia(Process_group)
573
    if (setpgid(m_pid, m_pid)) {
656
    if (setpgid(m->m_pid, m->m_pid)) {
574
        // This can fail with EACCES if the son has already done execve 
657
        // This can fail with EACCES if the son has already done execve 
575
        // (linux at least)
658
        // (linux at least)
576
        LOGDEB2(("ExecCmd: father setpgid(son)(%d,%d) errno %d (ok)\n",
659
        LOGDEB2(("ExecCmd: father setpgid(son)(%d,%d) errno %d (ok)\n",
577
                 m_pid, m_pid, errno));
660
                 m->m_pid, m->m_pid, errno));
578
    }
661
    }
579
662
580
    sigemptyset(&m_blkcld);
663
    sigemptyset(&m->m_blkcld);
581
    sigaddset(&m_blkcld, SIGCHLD);
664
    sigaddset(&m->m_blkcld, SIGCHLD);
582
    pthread_sigmask(SIG_BLOCK, &m_blkcld, 0);
665
    pthread_sigmask(SIG_BLOCK, &m->m_blkcld, 0);
583
666
584
    if (has_input) {
667
    if (has_input) {
585
    close(m_pipein[0]);
668
    close(m->m_pipein[0]);
586
    m_pipein[0] = -1;
669
    m->m_pipein[0] = -1;
587
    NetconCli *iclicon = new NetconCli();
670
    NetconCli *iclicon = new NetconCli();
588
    iclicon->setconn(m_pipein[1]);
671
    iclicon->setconn(m->m_pipein[1]);
589
  m_tocmd = NetconP(iclicon);
672
  m->m_tocmd = STD_SHARED_PTR<NetconCli>(iclicon);
590
    }
673
    }
591
    if (has_output) {
674
    if (has_output) {
592
    close(m_pipeout[1]);
675
    close(m->m_pipeout[1]);
593
    m_pipeout[1] = -1;
676
    m->m_pipeout[1] = -1;
594
    NetconCli *oclicon = new NetconCli();
677
    NetconCli *oclicon = new NetconCli();
595
    oclicon->setconn(m_pipeout[0]);
678
    oclicon->setconn(m->m_pipeout[0]);
596
  m_fromcmd = NetconP(oclicon);
679
  m->m_fromcmd = STD_SHARED_PTR<NetconCli>(oclicon);
597
    }
680
    }
598
681
599
    /* Don't want to undo what we just did ! */
682
    /* Don't want to undo what we just did ! */
600
    e.inactivate();
683
    e.inactivate();
601
684
...
...
603
}
686
}
604
687
605
// Netcon callback. Send data to the command's input
688
// Netcon callback. Send data to the command's input
606
class ExecWriter : public NetconWorker {
689
class ExecWriter : public NetconWorker {
607
public:
690
public:
608
    ExecWriter(const string *input, ExecCmdProvide *provide) 
691
    ExecWriter(const string *input, ExecCmdProvide *provide, 
692
               ExecCmd::Internal *parent)
609
    : m_input(input), m_cnt(0), m_provide(provide)
693
    : m_cmd(parent), m_input(input), m_cnt(0), m_provide(provide) {
610
    {}                    
694
    }
695
    void shutdown() {
696
        close(m_cmd->m_pipein[1]);
697
        m_cmd->m_pipein[1] = -1;
698
  m_cmd->m_tocmd.reset();
699
    }
611
    virtual int data(NetconData *con, Netcon::Event reason)
700
    virtual int data(NetconData *con, Netcon::Event reason)
612
    {
701
    {
613
    if (!m_input) return -1;
702
    if (!m_input) 
703
            return -1;
614
    LOGDEB1(("ExecWriter: input m_cnt %d input length %d\n", m_cnt, 
704
    LOGDEB1(("ExecWriter: input m_cnt %d input length %d\n", m_cnt, 
615
         m_input->length()));
705
         m_input->length()));
616
    if (m_cnt >= m_input->length()) {
706
    if (m_cnt >= m_input->length()) {
617
        // Fd ready for more but we got none.
707
        // Fd ready for more but we got none. Try to get data, else
708
            // shutdown;
618
        if (m_provide) {
709
        if (!m_provide) {
619
      m_provide->newData();
710
                shutdown();
620
      if (m_input->empty()) {
621
          return 0;
622
      } else {
623
          m_cnt = 0;
624
      }
625
      LOGDEB2(("ExecWriter: provide m_cnt %d input length %d\n", 
626
           m_cnt, m_input->length()));
627
      } else {
628
        return 0;
711
        return 0;
629
      }
712
            }
713
            m_provide->newData();
714
            if (m_input->empty()) {
715
                shutdown();
716
                return 0;
717
            } else {
718
                // Ready with new buffer, reset use count
719
                m_cnt = 0;
720
            }
721
            LOGDEB2(("ExecWriter: provide m_cnt %d input length %d\n", 
722
                     m_cnt, m_input->length()));
630
    }
723
    }
631
    int ret = con->send(m_input->c_str() + m_cnt, 
724
    int ret = con->send(m_input->c_str() + m_cnt, 
632
                m_input->length() - m_cnt);
725
                m_input->length() - m_cnt);
633
    LOGDEB2(("ExecWriter: wrote %d to command\n", ret));
726
    LOGDEB2(("ExecWriter: wrote %d to command\n", ret));
634
    if (ret <= 0) {
727
    if (ret <= 0) {
...
...
637
    }
730
    }
638
    m_cnt += ret;
731
    m_cnt += ret;
639
    return ret;
732
    return ret;
640
    }
733
    }
641
private:
734
private:
735
    ExecCmd::Internal *m_cmd;
642
    const string   *m_input;
736
    const string   *m_input;
643
    unsigned int    m_cnt; // Current offset inside m_input
737
    unsigned int    m_cnt; // Current offset inside m_input
644
    ExecCmdProvide *m_provide;
738
    ExecCmdProvide *m_provide;
645
};
739
};
646
740
...
...
677
    if (startExec(cmd, args, input != 0, output != 0) < 0) {
771
    if (startExec(cmd, args, input != 0, output != 0) < 0) {
678
    return -1;
772
    return -1;
679
    }
773
    }
680
774
681
    // Cleanup in case we return early
775
    // Cleanup in case we return early
682
    ExecCmdRsrc e(this);
776
    ExecCmdRsrc e(this->m);
683
    SelectLoop myloop;
777
    SelectLoop myloop;
684
    int ret = 0;
778
    int ret = 0;
685
    if (input || output) {
779
    if (input || output) {
686
        // Setup output
780
        // Setup output
687
    if (output) {
781
    if (output) {
688
        NetconCli *oclicon = dynamic_cast<NetconCli *>(m_fromcmd.get());
782
        NetconCli *oclicon = m->m_fromcmd.get();
689
        if (!oclicon) {
783
        if (!oclicon) {
690
        LOGERR(("ExecCmd::doexec: no connection from command\n"));
784
        LOGERR(("ExecCmd::doexec: no connection from command\n"));
691
        return -1;
785
        return -1;
692
        }
786
        }
693
        oclicon->setcallback(STD_SHARED_PTR<NetconWorker>
787
        oclicon->setcallback(STD_SHARED_PTR<NetconWorker>
694
                 (new ExecReader(output, m_advise)));
788
                 (new ExecReader(output, m->m_advise)));
695
        myloop.addselcon(m_fromcmd, Netcon::NETCONPOLL_READ);
789
        myloop.addselcon(m->m_fromcmd, Netcon::NETCONPOLL_READ);
696
        // Give up ownership 
790
        // Give up ownership 
697
        m_fromcmd.reset();
791
        m->m_fromcmd.reset();
698
    } 
792
    } 
699
        // Setup input
793
        // Setup input
700
    if (input) {
794
    if (input) {
701
        NetconCli *iclicon = dynamic_cast<NetconCli *>(m_tocmd.get());
795
        NetconCli *iclicon = m->m_tocmd.get();
702
        if (!iclicon) {
796
        if (!iclicon) {
703
        LOGERR(("ExecCmd::doexec: no connection from command\n"));
797
        LOGERR(("ExecCmd::doexec: no connection from command\n"));
704
        return -1;
798
        return -1;
705
        }
799
        }
706
        iclicon->setcallback(STD_SHARED_PTR<NetconWorker>
800
        iclicon->setcallback(STD_SHARED_PTR<NetconWorker>
707
                 (new ExecWriter(input, m_provide)));
801
                 (new ExecWriter(input, m->m_provide, m)));
708
        myloop.addselcon(m_tocmd, Netcon::NETCONPOLL_WRITE);
802
        myloop.addselcon(m->m_tocmd, Netcon::NETCONPOLL_WRITE);
709
        // Give up ownership 
803
        // Give up ownership 
710
        m_tocmd.reset();
804
        m->m_tocmd.reset();
711
    }
805
    }
712
806
713
        // Do the actual reading/writing/waiting
807
        // Do the actual reading/writing/waiting
714
    myloop.setperiodichandler(0, 0, m_timeoutMs);
808
    myloop.setperiodichandler(0, 0, m->m_timeoutMs);
715
    while ((ret = myloop.doLoop()) > 0) {
809
    while ((ret = myloop.doLoop()) > 0) {
716
        LOGDEB(("ExecCmd::doexec: selectloop returned %d\n", ret));
810
        LOGDEB(("ExecCmd::doexec: selectloop returned %d\n", ret));
717
        if (m_advise)
811
        if (m->m_advise)
718
        m_advise->newData(0);
812
        m->m_advise->newData(0);
719
        if (m_killRequest) {
813
        if (m->m_killRequest) {
720
        LOGINFO(("ExecCmd::doexec: cancel request\n"));
814
        LOGINFO(("ExecCmd::doexec: cancel request\n"));
721
        break;
815
        break;
722
        }
816
        }
723
    }
817
    }
724
    LOGDEB0(("ExecCmd::doexec: selectloop returned %d\n", ret));
818
    LOGDEB0(("ExecCmd::doexec: selectloop returned %d\n", ret));
725
        // Check for interrupt request: we won't want to waitpid()
819
        // Check for interrupt request: we won't want to waitpid()
726
        if (m_advise)
820
        if (m->m_advise)
727
            m_advise->newData(0);
821
            m->m_advise->newData(0);
728
822
729
        // The netcons don't take ownership of the fds: we have to close them
823
        // The netcons don't take ownership of the fds: we have to close them
730
        // (have to do it before wait, this may be the signal the child is 
824
        // (have to do it before wait, this may be the signal the child is 
731
        // waiting for exiting).
825
        // waiting for exiting).
732
        if (input) {
826
        if (input) {
733
            close(m_pipein[1]);
827
            close(m->m_pipein[1]);
734
            m_pipein[1] = -1;
828
            m->m_pipein[1] = -1;
735
        }
829
        }
736
        if (output) {
830
        if (output) {
737
            close(m_pipeout[0]);
831
            close(m->m_pipeout[0]);
738
            m_pipeout[0] = -1;
832
            m->m_pipeout[0] = -1;
739
        }
833
        }
740
    }
834
    }
741
835
742
    // Normal return: deactivate cleaner, wait() will do the cleanup
836
    // Normal return: deactivate cleaner, wait() will do the cleanup
743
    e.inactivate();
837
    e.inactivate();
...
...
748
    return ret1;
842
    return ret1;
749
}
843
}
750
844
751
int ExecCmd::send(const string& data)
845
int ExecCmd::send(const string& data)
752
{
846
{
753
    NetconCli *con = dynamic_cast<NetconCli *>(m_tocmd.get());
847
    NetconCli *con = m->m_tocmd.get();
754
    if (con == 0) {
848
    if (con == 0) {
755
    LOGERR(("ExecCmd::send: outpipe is closed\n"));
849
    LOGERR(("ExecCmd::send: outpipe is closed\n"));
756
    return -1;
850
    return -1;
757
    }
851
    }
758
    unsigned int nwritten = 0;
852
    unsigned int nwritten = 0;
759
    while (nwritten < data.length()) {
853
    while (nwritten < data.length()) {
760
    if (m_killRequest)
854
    if (m->m_killRequest)
761
        break;
855
        break;
762
    int n = con->send(data.c_str() + nwritten, data.length() - nwritten);
856
    int n = con->send(data.c_str() + nwritten, data.length() - nwritten);
763
    if (n < 0) {
857
    if (n < 0) {
764
        LOGERR(("ExecCmd::send: send failed\n"));
858
        LOGERR(("ExecCmd::send: send failed\n"));
765
        return -1;
859
        return -1;
...
...
769
    return nwritten;
863
    return nwritten;
770
}
864
}
771
865
772
int ExecCmd::receive(string& data, int cnt)
866
int ExecCmd::receive(string& data, int cnt)
773
{
867
{
774
    NetconCli *con = dynamic_cast<NetconCli *>(m_fromcmd.get());
868
    NetconCli *con = m->m_fromcmd.get();
775
    if (con == 0) {
869
    if (con == 0) {
776
    LOGERR(("ExecCmd::receive: inpipe is closed\n"));
870
    LOGERR(("ExecCmd::receive: inpipe is closed\n"));
777
    return -1;
871
    return -1;
778
    }
872
    }
779
    const int BS = 4096;
873
    const int BS = 4096;
...
...
796
    return ntot;
890
    return ntot;
797
}
891
}
798
892
799
int ExecCmd::getline(string& data)
893
int ExecCmd::getline(string& data)
800
{
894
{
801
    NetconCli *con = dynamic_cast<NetconCli *>(m_fromcmd.get());
895
    NetconCli *con = m->m_fromcmd.get();
802
    if (con == 0) {
896
    if (con == 0) {
803
    LOGERR(("ExecCmd::receive: inpipe is closed\n"));
897
    LOGERR(("ExecCmd::receive: inpipe is closed\n"));
804
    return -1;
898
    return -1;
805
    }
899
    }
806
    const int BS = 1024;
900
    const int BS = 1024;
...
...
815
    }
909
    }
816
    return n;
910
    return n;
817
}
911
}
818
912
819
// Wait for command status and clean up all resources.
913
// Wait for command status and clean up all resources.
914
// We would like to avoid blocking here too, but there is no simple
915
// way to do this. The 2 possible approaches would be to:
916
//  - Use signals (alarm), waitpid() is interruptible. but signals and
917
//    threads... This would need a specialized thread, inter-thread comms etc.
918
//  - Use an intermediary process when starting the command. The
919
//    process forks a timer process, and the real command, then calls
920
//    a blocking waitpid on all at the end, and is guaranteed to get
921
//    at least the timer process status, thus yielding a select()
922
//    equivalent. This is bad too, because the timeout is on the whole
923
//    exec, not just the wait
924
// Just calling waitpid() with WNOHANG with a sleep() between tries
925
// does not work: the first waitpid() usually comes too early and
926
// reaps nothing, resulting in almost always one sleep() or more.
927
// 
928
// So no timeout here. This has not been a problem in practise inside recoll.
929
// In case of need, using a semi-busy loop with short sleeps
930
// increasing from a few mS might work without creating too much
931
// overhead.
820
int ExecCmd::wait()
932
int ExecCmd::wait()
821
{
933
{
822
    ExecCmdRsrc e(this);
934
    ExecCmdRsrc e(this->m);
823
    int status = -1;
935
    int status = -1;
824
    if (!m_killRequest && m_pid > 0) {
936
    if (!m->m_killRequest && m->m_pid > 0) {
825
    if (waitpid(m_pid, &status, 0) < 0) {
937
    if (waitpid(m->m_pid, &status, 0) < 0) {
826
        LOGERR(("ExecCmd::waitpid: returned -1 errno %d\n", errno));
938
        LOGERR(("ExecCmd::waitpid: returned -1 errno %d\n", errno));
827
        status = -1;
939
        status = -1;
828
    }
940
    }
829
        LOGDEB(("ExecCmd::wait: got status 0x%x\n", status));
941
        LOGDEB(("ExecCmd::wait: got status 0x%x\n", status));
830
    m_pid = -1;
942
    m->m_pid = -1;
831
    }
943
    }
832
    // Let the ExecCmdRsrc cleanup
944
    // Let the ExecCmdRsrc cleanup, it will do the killing/waiting if needed
833
    return status;
945
    return status;
834
}
946
}
835
947
836
bool ExecCmd::maybereap(int *status)
948
bool ExecCmd::maybereap(int *status)
837
{
949
{
838
    ExecCmdRsrc e(this);
950
    ExecCmdRsrc e(this->m);
839
    *status = -1;
951
    *status = -1;
840
952
841
    if (m_pid <= 0) {
953
    if (m->m_pid <= 0) {
842
    // Already waited for ??
954
    // Already waited for ??
843
    return true;
955
    return true;
844
    }
956
    }
845
957
846
    pid_t pid = waitpid(m_pid, status, WNOHANG);
958
    pid_t pid = waitpid(m->m_pid, status, WNOHANG);
847
    if (pid < 0) {
959
    if (pid < 0) {
848
        LOGERR(("ExecCmd::maybereap: returned -1 errno %d\n", errno));
960
        LOGERR(("ExecCmd::maybereap: returned -1 errno %d\n", errno));
849
    m_pid = -1;
961
    m->m_pid = -1;
850
    return true;
962
    return true;
851
    } else if (pid == 0) {
963
    } else if (pid == 0) {
852
    LOGDEB1(("ExecCmd::maybereap: not exited yet\n"));
964
    LOGDEB1(("ExecCmd::maybereap: not exited yet\n"));
853
    e.inactivate();
965
    e.inactivate();
854
    return false;
966
    return false;
855
    } else {
967
    } else {
856
        LOGDEB(("ExecCmd::maybereap: got status 0x%x\n", status));
968
        LOGDEB(("ExecCmd::maybereap: got status 0x%x\n", status));
857
    m_pid = -1;
969
    m->m_pid = -1;
858
    return true;
970
    return true;
859
    }
971
    }
860
}
972
}
861
973
862
// Static
974
// Static
...
...
990
1102
991
#include <stdio.h>
1103
#include <stdio.h>
992
#include <stdlib.h>
1104
#include <stdlib.h>
993
#include <unistd.h>
1105
#include <unistd.h>
994
#include <string.h>
1106
#include <string.h>
1107
#include <signal.h>
995
1108
996
#include <string>
1109
#include <string>
997
#include <iostream>
1110
#include <iostream>
998
#include <sstream>
1111
#include <sstream>
999
#include <vector>
1112
#include <vector>
1000
using namespace std;
1001
1113
1002
#include "debuglog.h"
1114
#include "debuglog.h"
1003
#include "cancelcheck.h"
1115
#include "cancelcheck.h"
1004
#include "execmd.h"
1116
#include "execmd.h"
1005
#include "smallut.h"
1117
#include "smallut.h"
1006
1118
1007
// Testing with rclaudio: use an mp3 as parameter
1119
using namespace std;
1008
static const string tstcmd("/usr/share/recoll/filters/rclaudio");
1120
1009
static const string mimetype("audio/mpeg");
1121
1010
bool exercise_mhexecm(const string& filename)
1122
// Testing the rclexecm protocol outside of recoll. Here we use the
1123
// rcldoc.py filter, you can try with rclaudio too, adjust the file arg
1124
// accordingly
1125
bool exercise_mhexecm(const string& cmdstr, const string& mimetype, 
1126
                      vector<string>& files)
1011
{
1127
{
1012
    ExecCmd cmd;
1128
    ExecCmd cmd;
1013
1129
1014
    vector<string>myparams; 
1130
    vector<string> myparams; 
1015
1131
1016
    if (cmd.startExec(tstcmd, myparams, 1, 1) < 0) {
1132
    if (cmd.startExec(cmdstr, myparams, 1, 1) < 0) {
1017
    cerr << "startExec " << tstcmd << " failed. Missing command?\n";
1133
    cerr << "startExec " << cmdstr << " failed. Missing command?\n";
1018
    return false;
1134
    return false;
1019
    }
1135
    }
1020
1136
1137
    for (vector<string>::const_iterator it = files.begin();
1138
         it != files.end(); it++) {
1021
    // Build request message
1139
        // Build request message
1022
    ostringstream obuf;
1140
        ostringstream obuf;
1023
    obuf << "FileName: " << filename.length() << "\n" << filename;
1141
        obuf << "Filename: " << (*it).length() << "\n" << (*it);
1024
    obuf << "Mimetype: " << mimetype.length() << "\n" << mimetype;
1142
        obuf << "Mimetype: " << mimetype.length() << "\n" << mimetype;
1025
    // Bogus parameter should be skipped by filter
1143
        // Bogus parameter should be skipped by filter
1026
    obuf << "BogusParam: " << string("bogus").length() << "\n" << "bogus";
1144
        obuf << "BogusParam: " << string("bogus").length() << "\n" << "bogus";
1027
    obuf << "\n";
1145
        obuf << "\n";
1028
    cerr << "SENDING: [" << obuf.str() << "]\n";
1146
        cerr << "SENDING: [" << obuf.str() << "]\n";
1029
    // Send it 
1147
        // Send it 
1030
    if (cmd.send(obuf.str()) < 0) {
1148
        if (cmd.send(obuf.str()) < 0) {
1031
  // The real code calls zapchild here, but we don't need it as
1149
            // The real code calls zapchild here, but we don't need it as
1032
  // this will be handled by ~ExecCmd
1150
            // this will be handled by ~ExecCmd
1033
        //cmd.zapChild();
1151
            //cmd.zapChild();
1034
        cerr << "send error\n";
1152
            cerr << "send error\n";
1035
        return false;
1153
            return false;
1036
    }
1154
        }
1037
1155
1038
    // Read answer
1156
        // Read answer
1039
    for (int loop=0;;loop++) {
1157
        for (int loop=0;;loop++) {
1040
        string name, data;
1158
            string name, data;
1041
1159
1042
  // Code from mh_execm.cpp: readDataElement
1160
            // Code from mh_execm.cpp: readDataElement
1043
  string ibuf;
1161
            string ibuf;
1044
  // Read name and length
1162
            // Read name and length
1045
  if (cmd.getline(ibuf) <= 0) {
1163
            if (cmd.getline(ibuf) <= 0) {
1046
      cerr << "getline error\n";
1164
                cerr << "getline error\n";
1047
      return false;
1165
                return false;
1048
  }
1166
            }
1049
  // Empty line (end of message)
1167
            // Empty line (end of message)
1050
  if (!ibuf.compare("\n")) {
1168
            if (!ibuf.compare("\n")) {
1051
      cerr << "Got empty line\n";
1169
                cerr << "Got empty line\n";
1052
      name.clear();
1170
                name.clear();
1053
      return true;
1171
                break;
1054
  }
1172
            }
1055
1173
1056
  // Filters will sometimes abort before entering the real protocol, ie if
1174
            // Filters will sometimes abort before entering the real
1057
  // a module can't be loaded. Check the special filter error first word:
1175
            // protocol, ie if a module can't be loaded. Check the
1176
            // special filter error first word:
1058
  if (ibuf.find("RECFILTERROR ") == 0) {
1177
            if (ibuf.find("RECFILTERROR ") == 0) {
1059
      cerr << "Got RECFILTERROR\n";
1178
                cerr << "Got RECFILTERROR\n";
1060
      return false;
1179
                return false;
1061
  }
1180
            }
1062
1181
1063
  // We're expecting something like Name: len\n
1182
            // We're expecting something like Name: len\n
1064
  vector<string> tokens;
1183
            vector<string> tokens;
1065
  stringToTokens(ibuf, tokens);
1184
            stringToTokens(ibuf, tokens);
1066
  if (tokens.size() != 2) {
1185
            if (tokens.size() != 2) {
1067
      cerr << "bad line in filter output: [" << ibuf << "]\n";
1186
                cerr << "bad line in filter output: [" << ibuf << "]\n";
1068
      return false;
1187
                return false;
1069
  }
1188
            }
1070
  vector<string>::iterator it = tokens.begin();
1189
            vector<string>::iterator it = tokens.begin();
1071
  name = *it++;
1190
            name = *it++;
1072
  string& slen = *it;
1191
            string& slen = *it;
1073
  int len;
1192
            int len;
1074
  if (sscanf(slen.c_str(), "%d", &len) != 1) {
1193
            if (sscanf(slen.c_str(), "%d", &len) != 1) {
1075
      cerr << "bad line in filter output (no len): [" << ibuf << "]\n";
1194
                cerr << "bad line in filter output (no len): [" << 
1076
      return false;
1195
                    ibuf << "]\n";
1077
  }
1196
                return false;
1197
            }
1078
  // Read element data
1198
            // Read element data
1079
  data.erase();
1199
            data.erase();
1080
  if (len > 0 && cmd.receive(data, len) != len) {
1200
            if (len > 0 && cmd.receive(data, len) != len) {
1081
      cerr << "MHExecMultiple: expected " << len << 
1201
                cerr << "MHExecMultiple: expected " << len << 
1082
      " bytes of data, got " << data.length() << endl;
1202
                    " bytes of data, got " << data.length() << endl;
1083
      return false;
1203
                return false;
1084
  }
1204
            }
1085
1205
1086
  // Empty element: end of message
1206
            // Empty element: end of message
1087
        if (name.empty())
1207
            if (name.empty())
1088
            break;
1208
                break;
1089
  cerr << "Got name: [" << name << "] data [" << data << "]\n";
1209
            cerr << "Got name: [" << name << "] data [" << data << "]\n";
1090
    }
1210
        }
1091
1211
    }
1092
    return true;
1212
    return true;
1093
}
1213
}
1094
1214
1215
static char *thisprog;
1216
static char usage [] =
1217
"trexecmd [-c -r -i -o] cmd [arg1 arg2 ...]\n" 
1218
"   -c : test cancellation (ie: trexecmd -c sleep 1000)\n"
1219
"   -r : run reexec. Must be separate option.\n"
1220
"   -i : command takes input\n"
1221
"   -o : command produces output\n"
1222
"    If -i is set, we send /etc/group contents to whatever command is run\n"
1223
"    If -o is set, we print whatever comes out\n"
1224
"trexecmd -m <filter> <mimetype> <file> [file ...]: test execm:\n"
1225
"     <filter> should be the path to an execm filter\n"
1226
"     <mimetype> the type of the file parameters\n"
1227
"trexecmd -w cmd : do the 'which' thing\n"
1228
;
1229
1230
static void Usage(void)
1231
{
1232
    fprintf(stderr, "%s: usage:\n%s", thisprog, usage);
1233
    exit(1);
1234
}
1095
1235
1096
static int     op_flags;
1236
static int     op_flags;
1097
#define OPT_MOINS 0x1
1237
#define OPT_MOINS 0x1
1098
#define OPT_b   0x4 
1238
#define OPT_i     0x4
1099
#define OPT_w     0x8
1239
#define OPT_w     0x8
1100
#define OPT_c     0x10
1240
#define OPT_c     0x10
1101
#define OPT_r     0x20
1241
#define OPT_r     0x20
1102
#define OPT_m     0x40
1242
#define OPT_m     0x40
1243
#define OPT_o     0x80
1103
1244
1104
const char *data = "Une ligne de donnees\n";
1245
// Data sink for data coming out of the command. We also use it to set
1246
// a cancellation after a moment.
1105
class MEAdv : public ExecCmdAdvise {
1247
class MEAdv : public ExecCmdAdvise {
1106
public:
1248
public:
1107
    ExecCmd *cmd;
1108
    void newData(int cnt) {
1249
    void newData(int cnt) {
1109
    if (op_flags & OPT_c) {
1250
    if (op_flags & OPT_c) {
1110
        static int  callcnt;
1251
        static int  callcnt;
1111
        if (callcnt++ == 3) {
1252
        if (callcnt++ == 10) {
1112
      throw CancelExcept();
1253
                // Just sets the cancellation flag
1254
      CancelCheck::instance().setCancel();
1255
                // Would be called from somewhere else and throws an
1256
                // exception. We call it here for simplicity
1257
                CancelCheck::instance().checkCancel();
1113
        }
1258
        }
1114
    }
1259
    }
1115
    cerr << "newData(" << cnt << ")" << endl;
1260
    cerr << "newData(" << cnt << ")" << endl;
1116
  //  CancelCheck::instance().setCancel();
1117
  //  CancelCheck::instance().checkCancel();
1118
  //  cmd->setCancel();
1119
    }
1261
    }
1120
};
1262
};
1121
1263
1264
// Data provider, used if the -i flag is set
1122
class MEPv : public ExecCmdProvide {
1265
class MEPv : public ExecCmdProvide {
1123
public:
1266
public:
1124
    FILE *m_fp;
1267
    FILE *m_fp;
1125
    string *m_input;
1268
    string *m_input;
1126
    MEPv(string *i) 
1269
    MEPv(string *i) 
...
...
1141
    }
1284
    }
1142
    }
1285
    }
1143
};
1286
};
1144
1287
1145
1288
1146
static char *thisprog;
1147
static char usage [] =
1148
"trexecmd [-c|-r] cmd [arg1 arg2 ...]\n" 
1149
" -c : test cancellation (ie: trexecmd -c sleep 1000)\n"
1150
" -r : test reexec\n"
1151
" -m <path to mp3 file>: test execm: needs installed and working rclaudio/mutagen\n"
1152
"trexecmd -w cmd : do the which thing\n"
1153
;
1154
static void Usage(void)
1155
{
1156
    fprintf(stderr, "%s: usage:\n%s", thisprog, usage);
1157
    exit(1);
1158
}
1159
1289
1160
ReExec reexec;
1290
ReExec reexec;
1161
1162
int main(int argc, char *argv[])
1291
int main(int argc, char *argv[])
1163
{
1292
{
1164
    reexec.init(argc, argv);
1293
    reexec.init(argc, argv);
1165
1294
1166
    if (0) {
1295
    if (0) {
...
...
1186
        switch (*(*argv)++) {
1315
        switch (*(*argv)++) {
1187
        case 'c':   op_flags |= OPT_c; break;
1316
        case 'c':   op_flags |= OPT_c; break;
1188
        case 'r':   op_flags |= OPT_r; break;
1317
        case 'r':   op_flags |= OPT_r; break;
1189
        case 'w':   op_flags |= OPT_w; break;
1318
        case 'w':   op_flags |= OPT_w; break;
1190
        case 'm':   op_flags |= OPT_m; break;
1319
        case 'm':   op_flags |= OPT_m; break;
1320
      case 'i':   op_flags |= OPT_i; break;
1321
      case 'o':   op_flags |= OPT_o; break;
1191
        default: Usage();   break;
1322
        default: Usage();   break;
1192
        }
1323
        }
1193
    b1: argc--; argv++;
1324
    b1: argc--; argv++;
1194
    }
1325
    }
1195
1326
...
...
1199
    string arg1 = *argv++; argc--;
1330
    string arg1 = *argv++; argc--;
1200
    vector<string> l;
1331
    vector<string> l;
1201
    while (argc > 0) {
1332
    while (argc > 0) {
1202
    l.push_back(*argv++); argc--;
1333
    l.push_back(*argv++); argc--;
1203
    }
1334
    }
1335
1204
    DebugLog::getdbl()->setloglevel(DEBDEB1);
1336
    DebugLog::getdbl()->setloglevel(DEBDEB1);
1205
    DebugLog::setfilename("stderr");
1337
    DebugLog::setfilename("stderr");
1206
    signal(SIGPIPE, SIG_IGN);
1338
    signal(SIGPIPE, SIG_IGN);
1207
1339
1208
    if (op_flags & OPT_r) {
1340
    if (op_flags & OPT_r) {
1209
  // Test reexec
1341
  // Test reexec. Normally only once, next time we fall through
1342
        // because we remove the -r option (only works if it was isolated, not like -rc
1210
    chdir("/");
1343
    chdir("/");
1211
        argv[0] = strdup("");
1344
        argv[0] = strdup("");
1212
    sleep(1);
1345
    sleep(1);
1346
        cerr << "Calling reexec\n";
1347
        // We remove the -r arg from list, otherwise we are going to
1348
        // loop (which you can try by commenting out the following
1349
        // line)
1350
        reexec.removeArg("-r");
1213
        reexec.reexec();
1351
        reexec.reexec();
1214
    }
1352
    }
1215
1353
1216
    if (op_flags & OPT_w) {
1354
    if (op_flags & OPT_w) {
1217
    // Test "which" method
1355
    // Test "which" method
1218
    string path;
1356
    string path;
1219
    if (ExecCmd::which(arg1, path)) {
1357
    if (ExecCmd::which(arg1, path)) {
1220
        cout << path << endl;
1358
        cout << path << endl;
1221
      exit(0);
1359
            return 0;
1222
    } 
1360
    } 
1223
  exit(1);
1361
  return 1;
1224
    }
1225
1226
    if (op_flags & OPT_m) {
1362
    } else if (op_flags & OPT_m) {
1363
        if (l.size() < 2)
1364
            Usage();
1365
        string mimetype = l[0];
1366
        l.erase(l.begin());
1227
    return exercise_mhexecm(arg1) ? 0 : 1;
1367
    return exercise_mhexecm(arg1, mimetype, l) ? 0 : 1;
1228
    }
1368
    } else {
1229
1230
    //////////////
1231
    // Default: execute command line arguments
1369
        // Default: execute command line arguments
1232
    ExecCmd mexec;
1370
        ExecCmd mexec;
1371
1372
        // Set callback to be called whenever there is new data
1373
        // available and at a periodic interval, to check for
1374
        // cancellation
1233
    MEAdv adv;
1375
        MEAdv adv;
1234
    adv.cmd = &mexec;
1235
    mexec.setAdvise(&adv);
1376
        mexec.setAdvise(&adv);
1236
    mexec.setTimeout(5);
1377
        mexec.setTimeout(5);
1378
1379
        // Stderr output goes there
1237
    mexec.setStderr("/tmp/trexecStderr");
1380
        mexec.setStderr("/tmp/trexecStderr");
1381
        
1382
        // A few environment variables. Check with trexecmd env
1238
    mexec.putenv("TESTVARIABLE1=TESTVALUE1");
1383
        mexec.putenv("TESTVARIABLE1=TESTVALUE1");
1239
    mexec.putenv("TESTVARIABLE2=TESTVALUE2");
1384
        mexec.putenv("TESTVARIABLE2=TESTVALUE2");
1240
    mexec.putenv("TESTVARIABLE3=TESTVALUE3");
1385
        mexec.putenv("TESTVARIABLE3=TESTVALUE3");
1241
1386
1242
    string input, output;
1387
        string input, output;
1243
    //    input = data;
1244
    string *ip = 0;
1245
    ip = &input;
1246
1247
    MEPv  pv(&input);
1388
        MEPv  pv(&input);
1389
        
1390
        string *ip = 0;
1391
        if (op_flags  & OPT_i) {
1392
            ip = &input;
1248
    mexec.setProvide(&pv);
1393
            mexec.setProvide(&pv);
1394
        }
1395
        string *op = 0;
1396
        if (op_flags & OPT_o) {
1397
            op = &output;
1398
        }
1249
1399
1250
    int status = -1;
1400
        int status = -1;
1251
    try {
1401
        try {
1252
  status = mexec.doexec(arg1, l, ip, &output);
1402
            status = mexec.doexec(arg1, l, ip, op);
1253
    } catch (CancelExcept) {
1403
        } catch (CancelExcept) {
1254
  cerr << "CANCELLED" << endl;
1404
            cerr << "CANCELLED" << endl;
1255
    }
1405
        }
1256
1406
1257
    fprintf(stderr, "Status: 0x%x\n", status);
1407
        fprintf(stderr, "Status: 0x%x\n", status);
1408
        if (op_flags & OPT_o) {
1258
    cout << output;
1409
            cout << output;
1410
        }
1259
    exit (status >> 8);
1411
        exit (status >> 8);
1412
    }
1260
}
1413
}
1261
#endif // TEST
1414
#endif // TEST