Switch to unified view

a b/src/execmd.cpp
1
/* Copyright (C) 2004 J.F.Dockes
2
 *   This program is free software; you can redistribute it and/or modify
3
 *   it under the terms of the GNU General Public License as published by
4
 *   the Free Software Foundation; either version 2 of the License, or
5
 *   (at your option) any later version.
6
 *
7
 *   This program is distributed in the hope that it will be useful,
8
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10
 *   GNU General Public License for more details.
11
 *
12
 *   You should have received a copy of the GNU General Public License
13
 *   along with this program; if not, write to the
14
 *   Free Software Foundation, Inc.,
15
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
16
 */
17
#ifndef TEST_EXECMD
18
#include "config.h"
19
20
#include <stdio.h>
21
#include <stdlib.h>
22
#include <unistd.h>
23
#include <sys/stat.h>
24
#include <sys/types.h>
25
#include <sys/wait.h>
26
#include <sys/select.h>
27
#include <fcntl.h>
28
#include <errno.h>
29
#include <signal.h>
30
#include <time.h>
31
32
#include <vector>
33
#include <string>
34
#include <sstream>
35
#include <iostream>
36
37
#include "execmd.h"
38
39
#include "netcon.h"
40
#include "closefrom.h"
41
42
using namespace std;
43
44
extern char **environ;
45
46
bool ExecCmd::o_useVfork = false;
47
48
#ifdef RECOLL_DATADIR
49
#include "debuglog.h"
50
#include "smallut.h"
51
52
#else
53
// If compiling outside of recoll, make the file as standalone as reasonable.
54
55
#define LOGFATAL(X)
56
#define LOGERR(X)
57
#define LOGINFO(X)
58
#define LOGDEB(X)
59
#define LOGDEB0(X)
60
#define LOGDEB1(X)
61
#define LOGDEB2(X)
62
#define LOGDEB3(X)
63
#define LOGDEB4(X)
64
65
#ifndef MIN
66
#define MIN(A,B) ((A) < (B) ? (A) : (B))
67
#endif
68
69
static void stringToTokens(const string &s, vector<string> &tokens, 
70
                    const string &delims = " \t", bool skipinit=true);
71
72
static void stringToTokens(const string& str, vector<string>& tokens,
73
          const string& delims, bool skipinit)
74
{
75
    string::size_type startPos = 0, pos;
76
77
    // Skip initial delims, return empty if this eats all.
78
    if (skipinit && 
79
  (startPos = str.find_first_not_of(delims, 0)) == string::npos) {
80
  return;
81
    }
82
    while (startPos < str.size()) { 
83
        // Find next delimiter or end of string (end of token)
84
        pos = str.find_first_of(delims, startPos);
85
86
        // Add token to the vector and adjust start
87
  if (pos == string::npos) {
88
      tokens.push_back(str.substr(startPos));
89
      break;
90
  } else if (pos == startPos) {
91
      // Dont' push empty tokens after first
92
      if (tokens.empty())
93
      tokens.push_back(string());
94
      startPos = ++pos;
95
  } else {
96
      tokens.push_back(str.substr(startPos, pos - startPos));
97
      startPos = ++pos;
98
  }
99
    }
100
}
101
#endif // RECOLL_DATADIR
102
103
/* From FreeBSD's which command */
104
static bool exec_is_there(const char *candidate)
105
{
106
    struct stat fin;
107
108
    /* XXX work around access(2) false positives for superuser */
109
    if (access(candidate, X_OK) == 0 &&
110
  stat(candidate, &fin) == 0 &&
111
  S_ISREG(fin.st_mode) &&
112
  (getuid() != 0 ||
113
   (fin.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0)) {
114
  return true;
115
    }
116
    return false;
117
}
118
119
bool ExecCmd::which(const string& cmd, string& exepath, const char* path)
120
{
121
    if (cmd.empty()) 
122
  return false;
123
    if (cmd[0] == '/') {
124
  if (exec_is_there(cmd.c_str())) {
125
      exepath = cmd;
126
      return true;
127
  } else {
128
      return false;
129
  }
130
    }
131
132
    const char *pp;
133
    if (path) {
134
  pp = path;
135
    } else {
136
  pp = getenv("PATH");
137
    }
138
    if (pp == 0)
139
  return false;
140
141
    vector<string> pels;
142
    stringToTokens(pp, pels, ":");
143
    for (vector<string>::iterator it = pels.begin(); it != pels.end(); it++) {
144
  if (it->empty())
145
      *it = ".";
146
  string candidate = (it->empty() ? string(".") : *it) + "/" + cmd;
147
  if (exec_is_there(candidate.c_str())) {
148
      exepath = candidate;
149
      return true;
150
  }
151
    }
152
    return false;
153
}
154
155
void  ExecCmd::putenv(const string &ea)
156
{
157
    m_env.push_back(ea);
158
}
159
160
void  ExecCmd::putenv(const string &name, const string& value)
161
{
162
    string ea = name + "=" + value;
163
    putenv(ea);
164
}
165
166
static void msleep(int millis)
167
{
168
    struct timespec spec;
169
    spec.tv_sec = millis / 1000;
170
    spec.tv_nsec = (millis % 1000) * 1000000;
171
    nanosleep(&spec, 0);
172
}
173
174
/** A resource manager to ensure that execcmd cleans up if an exception is 
175
 *  raised in the callback, or at different places on errors occurring
176
 *  during method executions */
177
class ExecCmdRsrc {
178
public:
179
    ExecCmdRsrc(ExecCmd *parent) : m_parent(parent), m_active(true) {}
180
    void inactivate() {m_active = false;}
181
    ~ExecCmdRsrc() {
182
  if (!m_active || !m_parent)
183
      return;
184
  LOGDEB1(("~ExecCmdRsrc: working. mypid: %d\n", (int)getpid()));
185
186
  // Better to close the descs first in case the child is waiting in read
187
  if (m_parent->m_pipein[0] >= 0)
188
      close(m_parent->m_pipein[0]);
189
  if (m_parent->m_pipein[1] >= 0)
190
      close(m_parent->m_pipein[1]);
191
  if (m_parent->m_pipeout[0] >= 0)
192
      close(m_parent->m_pipeout[0]);
193
  if (m_parent->m_pipeout[1] >= 0)
194
      close(m_parent->m_pipeout[1]);
195
196
  // It's apparently possible for m_pid to be > 0 and getpgid to fail. In
197
  // this case, we have to conclude that the child process does 
198
  // not exist. Not too sure what causes this, but the previous code
199
  // definitely tried to call killpg(-1,) from time to time.
200
  pid_t grp;
201
  if (m_parent->m_pid > 0 && (grp = getpgid(m_parent->m_pid)) > 0) {
202
      LOGDEB(("ExecCmd: killpg(%d, SIGTERM)\n", grp));
203
            int ret = killpg(grp, SIGTERM);
204
      if (ret == 0) {
205
      for (int i = 0; i < 3; i++) {
206
          msleep(i == 0 ? 5 : (i == 1 ? 100 : 2000));
207
          int status;
208
          (void)waitpid(m_parent->m_pid, &status, WNOHANG);
209
          if (kill(m_parent->m_pid, 0) != 0)
210
          break;
211
          if (i == 2) {
212
          LOGDEB(("ExecCmd: killpg(%d, SIGKILL)\n", grp));
213
          killpg(grp, SIGKILL);
214
          (void)waitpid(m_parent->m_pid, &status, WNOHANG);
215
          }
216
      }
217
      } else {
218
                LOGERR(("ExecCmd: error killing process group %d: %d\n",
219
                        grp, errno));
220
            }
221
  }
222
  m_parent->m_tocmd = shared_ptr<Netcon>(0);
223
  m_parent->m_fromcmd = shared_ptr<Netcon>(0);
224
  pthread_sigmask(SIG_UNBLOCK, &m_parent->m_blkcld, 0);
225
  m_parent->reset();
226
    }
227
private:
228
    ExecCmd *m_parent;
229
    bool    m_active;
230
};
231
232
ExecCmd::~ExecCmd()
233
{
234
    ExecCmdRsrc(this);
235
}
236
237
// In child process. Set up pipes and exec command. 
238
// This must not return. _exit() on error.
239
// *** This can be called after a vfork, so no modification of the
240
//     process memory at all is allowed ***
241
// The LOGXX calls should not be there, but they occur only after "impossible"
242
// errors, which we would most definitely want to have a hint about.
243
//
244
// Note that any of the LOGXX calls could block on a mutex set in the
245
// father process, so that only absolutely exceptional conditions, 
246
// should be logged, for debugging and post-mortem purposes
247
// If one of the calls block, the problem manifests itself by 20mn
248
// (filter timeout) of looping on "ExecCmd::doexec: selectloop
249
// returned 1', because the father is waiting on the read descriptor
250
inline void ExecCmd::dochild(const string &cmd, const char **argv,
251
               const char **envv,
252
               bool has_input, bool has_output)
253
{
254
    // Start our own process group
255
    if (setpgid(0, getpid())) {
256
  LOGINFO(("ExecCmd::DOCHILD: setpgid(0, %d) failed: errno %d\n",
257
       getpid(), errno));
258
    }
259
260
    // Restore SIGTERM to default. Really, signal handling should be
261
    // specified when creating the execmd. Help Recoll get rid of its
262
    // filter children though. To be fixed one day... Not sure that
263
    // all of this is needed. But an ignored sigterm and the masks are
264
    // normally inherited.
265
    if (signal(SIGTERM, SIG_DFL) == SIG_ERR) {
266
  //LOGERR(("ExecCmd::DOCHILD: signal() failed, errno %d\n", errno));
267
    }
268
    sigset_t sset;
269
    sigfillset(&sset);
270
    pthread_sigmask(SIG_UNBLOCK, &sset, 0);
271
    sigprocmask(SIG_UNBLOCK, &sset, 0);
272
273
    if (has_input) {
274
  close(m_pipein[1]);
275
  if (m_pipein[0] != 0) {
276
      dup2(m_pipein[0], 0);
277
      close(m_pipein[0]);
278
  }
279
    }
280
    if (has_output) {
281
  close(m_pipeout[0]);
282
  if (m_pipeout[1] != 1) {
283
      if (dup2(m_pipeout[1], 1) < 0) {
284
      LOGERR(("ExecCmd::DOCHILD: dup2() failed. errno %d\n", errno));
285
      }
286
      if (close(m_pipeout[1]) < 0) {
287
      LOGERR(("ExecCmd::DOCHILD: close() failed. errno %d\n", errno));
288
      }
289
  }
290
    }
291
    // Do we need to redirect stderr ?
292
    if (!m_stderrFile.empty()) {
293
  int fd = open(m_stderrFile.c_str(), O_WRONLY|O_CREAT
294
#ifdef O_APPEND
295
            |O_APPEND
296
#endif
297
            , 0600);
298
  if (fd < 0) {
299
      close(2);
300
  } else {
301
      if (fd != 2) {
302
      dup2(fd, 2);
303
      }
304
      lseek(2, 0, 2);
305
  }
306
    }
307
308
    // Close all descriptors except 0,1,2
309
    libclf_closefrom(3);
310
311
    execve(cmd.c_str(), (char *const*)argv, (char *const*)envv);
312
    // Hu ho. This should never happened as we checked the existence of the
313
    // executable before calling dochild... Until we did this, this was 
314
    // the chief cause of LOG mutex deadlock
315
    LOGERR(("ExecCmd::DOCHILD: execve(%s) failed. errno %d\n", cmd.c_str(),
316
      errno));
317
    _exit(127);
318
}
319
320
int ExecCmd::startExec(const string &cmd, const vector<string>& args,
321
             bool has_input, bool has_output)
322
{
323
    { // Debug and logging
324
  string command = cmd + " ";
325
  for (vector<string>::const_iterator it = args.begin();it != args.end();
326
       it++) {
327
      command += "{" + *it + "} ";
328
  }
329
  LOGDEB(("ExecCmd::startExec: (%d|%d) %s\n", 
330
      has_input, has_output, command.c_str()));
331
    }
332
333
    // The resource manager ensures resources are freed if we return early
334
    ExecCmdRsrc e(this);
335
336
    if (has_input && pipe(m_pipein) < 0) {
337
  LOGERR(("ExecCmd::startExec: pipe(2) failed. errno %d\n", errno));
338
  return -1;
339
    }
340
    if (has_output && pipe(m_pipeout) < 0) {
341
  LOGERR(("ExecCmd::startExec: pipe(2) failed. errno %d\n", errno));
342
  return -1;
343
    }
344
345
346
//////////// vfork setup section
347
    // We do here things that we could/should do after a fork(), but
348
    // not a vfork(). Does no harm to do it here in both cases, except
349
    // that it needs cleanup (as compared to doing it just before
350
    // exec()).
351
352
    // Allocate arg vector (2 more for arg0 + final 0)
353
    typedef const char *Ccharp;
354
    Ccharp *argv;
355
    argv = (Ccharp *)malloc((args.size()+2) * sizeof(char *));
356
    if (argv == 0) {
357
  LOGERR(("ExecCmd::doexec: malloc() failed. errno %d\n", errno));
358
        return -1;
359
    }
360
    // Fill up argv
361
    argv[0] = cmd.c_str();
362
    int i = 1;
363
    vector<string>::const_iterator it;
364
    for (it = args.begin(); it != args.end(); it++) {
365
  argv[i++] = it->c_str();
366
    }
367
    argv[i] = 0;
368
369
    Ccharp *envv;
370
    int envsize;
371
    for (envsize = 0; ; envsize++) 
372
  if (environ[envsize] == 0)
373
      break;
374
    envv = (Ccharp *)malloc((envsize + m_env.size() + 2) * sizeof(char *));
375
    if (envv == 0) {
376
  LOGERR(("ExecCmd::doexec: malloc() failed. errno %d\n", errno));
377
        free(argv);
378
        return -1;
379
    }
380
    int eidx;
381
    for (eidx = 0; eidx < envsize; eidx++)
382
  envv[eidx] = environ[eidx];
383
    for (vector<string>::const_iterator it = m_env.begin(); 
384
   it != m_env.end(); it++) {
385
  envv[eidx++] = it->c_str();
386
    }
387
    envv[eidx] = 0;
388
389
    // As we are going to use execve, not execvp, do the PATH thing.
390
    string exe;
391
    if (!which(cmd, exe)) {
392
        LOGERR(("ExecCmd::startExec: %s not found\n", cmd.c_str()));
393
        free(argv);
394
        free(envv);
395
        return -1;
396
    }
397
////////////////////////////////
398
399
    if (o_useVfork) {
400
  m_pid = vfork();
401
    } else {
402
  m_pid = fork();
403
    }
404
    if (m_pid < 0) {
405
  LOGERR(("ExecCmd::startExec: fork(2) failed. errno %d\n", errno));
406
  return -1;
407
    }
408
    if (m_pid == 0) {
409
  // e.inactivate() is not needed. As we do not return, the call
410
  // stack won't be unwound and destructors of local objects
411
  // won't be called.
412
  dochild(exe, argv, envv, has_input, has_output);
413
  // dochild does not return. Just in case...
414
  _exit(1);
415
    }
416
417
    // Father process
418
419
////////////////////
420
    // Vfork cleanup section
421
    free(argv);
422
    free(envv);
423
///////////////////
424
425
    // Set the process group for the child. This is also done in the
426
    // child process see wikipedia(Process_group)
427
    if (setpgid(m_pid, m_pid)) {
428
        // This can fail with EACCES if the son has already done execve 
429
        // (linux at least)
430
        LOGDEB2(("ExecCmd: father setpgid(son)(%d,%d) errno %d (ok)\n",
431
                 m_pid, m_pid, errno));
432
    }
433
434
    sigemptyset(&m_blkcld);
435
    sigaddset(&m_blkcld, SIGCHLD);
436
    pthread_sigmask(SIG_BLOCK, &m_blkcld, 0);
437
438
    if (has_input) {
439
  close(m_pipein[0]);
440
  m_pipein[0] = -1;
441
  NetconCli *iclicon = new NetconCli();
442
  iclicon->setconn(m_pipein[1]);
443
  m_tocmd = NetconP(iclicon);
444
    }
445
    if (has_output) {
446
  close(m_pipeout[1]);
447
  m_pipeout[1] = -1;
448
  NetconCli *oclicon = new NetconCli();
449
  oclicon->setconn(m_pipeout[0]);
450
  m_fromcmd = NetconP(oclicon);
451
    }
452
453
    /* Don't want to undo what we just did ! */
454
    e.inactivate();
455
456
    return 0;
457
}
458
459
// Netcon callback. Send data to the command's input
460
class ExecWriter : public NetconWorker {
461
public:
462
    ExecWriter(const string *input, ExecCmdProvide *provide) 
463
  : m_input(input), m_cnt(0), m_provide(provide)
464
    {}                    
465
    virtual int data(NetconData *con, Netcon::Event reason)
466
    {
467
  if (!m_input) return -1;
468
  LOGDEB1(("ExecWriter: input m_cnt %d input length %d\n", m_cnt, 
469
       m_input->length()));
470
  if (m_cnt >= m_input->length()) {
471
      // Fd ready for more but we got none.
472
      if (m_provide) {
473
      m_provide->newData();
474
      if (m_input->empty()) {
475
          return 0;
476
      } else {
477
          m_cnt = 0;
478
      }
479
      LOGDEB2(("ExecWriter: provide m_cnt %d input length %d\n", 
480
           m_cnt, m_input->length()));
481
      } else {
482
      return 0;
483
      }
484
  }
485
  int ret = con->send(m_input->c_str() + m_cnt, 
486
              m_input->length() - m_cnt);
487
  LOGDEB2(("ExecWriter: wrote %d to command\n", ret));
488
  if (ret <= 0) {
489
      LOGERR(("ExecWriter: data: can't write\n"));
490
      return -1;
491
  }
492
  m_cnt += ret;
493
  return ret;
494
    }
495
private:
496
    const string   *m_input;
497
    unsigned int    m_cnt; // Current offset inside m_input
498
    ExecCmdProvide *m_provide;
499
};
500
501
// Netcon callback. Get data from the command output.
502
class ExecReader : public NetconWorker {
503
public:
504
    ExecReader(string *output, ExecCmdAdvise *advise) 
505
  : m_output(output), m_advise(advise)
506
    {}                    
507
    virtual int data(NetconData *con, Netcon::Event reason)
508
    {
509
  char buf[8192];
510
  int n = con->receive(buf, 8192);
511
  LOGDEB1(("ExecReader: got %d from command\n", n));
512
  if (n < 0) {
513
      LOGERR(("ExecCmd::doexec: receive failed. errno %d\n", errno));
514
  } else if (n > 0) {
515
      m_output->append(buf, n);
516
      if (m_advise)
517
      m_advise->newData(n);
518
  } // else n == 0, just return
519
  return n;
520
    }
521
private:
522
    string        *m_output;
523
    ExecCmdAdvise *m_advise;
524
};
525
526
527
int ExecCmd::doexec(const string &cmd, const vector<string>& args,
528
          const string *input, string *output)
529
{
530
531
    if (startExec(cmd, args, input != 0, output != 0) < 0) {
532
  return -1;
533
    }
534
535
    // Cleanup in case we return early
536
    ExecCmdRsrc e(this);
537
    SelectLoop myloop;
538
    int ret = 0;
539
    if (input || output) {
540
        // Setup output
541
  if (output) {
542
      NetconCli *oclicon = dynamic_cast<NetconCli *>(m_fromcmd.get());
543
      if (!oclicon) {
544
      LOGERR(("ExecCmd::doexec: no connection from command\n"));
545
      return -1;
546
      }
547
      oclicon->setcallback(make_shared<ExecReader>
548
               (ExecReader(output, m_advise)));
549
      myloop.addselcon(m_fromcmd, Netcon::NETCONPOLL_READ);
550
      // Give up ownership 
551
      m_fromcmd = shared_ptr<Netcon>(0);
552
  } 
553
        // Setup input
554
  if (input) {
555
      NetconCli *iclicon = dynamic_cast<NetconCli *>(m_tocmd.get());
556
      if (!iclicon) {
557
      LOGERR(("ExecCmd::doexec: no connection from command\n"));
558
      return -1;
559
      }
560
      iclicon->setcallback(make_shared<ExecWriter>
561
               (ExecWriter(input, m_provide)));
562
      myloop.addselcon(m_tocmd, Netcon::NETCONPOLL_WRITE);
563
      // Give up ownership 
564
      m_tocmd = shared_ptr<Netcon>(0);
565
  }
566
567
        // Do the actual reading/writing/waiting
568
  myloop.setperiodichandler(0, 0, m_timeoutMs);
569
  while ((ret = myloop.doLoop()) > 0) {
570
      LOGDEB(("ExecCmd::doexec: selectloop returned %d\n", ret));
571
      if (m_advise)
572
      m_advise->newData(0);
573
      if (m_killRequest) {
574
      LOGINFO(("ExecCmd::doexec: cancel request\n"));
575
      break;
576
      }
577
  }
578
  LOGDEB0(("ExecCmd::doexec: selectloop returned %d\n", ret));
579
        // Check for interrupt request: we won't want to waitpid()
580
        if (m_advise)
581
            m_advise->newData(0);
582
583
        // The netcons don't take ownership of the fds: we have to close them
584
        // (have to do it before wait, this may be the signal the child is 
585
        // waiting for exiting).
586
        if (input) {
587
            close(m_pipein[1]);
588
            m_pipein[1] = -1;
589
        }
590
        if (output) {
591
            close(m_pipeout[0]);
592
            m_pipeout[0] = -1;
593
        }
594
    }
595
596
    // Normal return: deactivate cleaner, wait() will do the cleanup
597
    e.inactivate();
598
599
    int ret1 = ExecCmd::wait();
600
    if (ret)
601
  return -1;
602
    return ret1;
603
}
604
605
int ExecCmd::send(const string& data)
606
{
607
    NetconCli *con = dynamic_cast<NetconCli *>(m_tocmd.get());
608
    if (con == 0) {
609
  LOGERR(("ExecCmd::send: outpipe is closed\n"));
610
  return -1;
611
    }
612
    unsigned int nwritten = 0;
613
    while (nwritten < data.length()) {
614
  if (m_killRequest)
615
      break;
616
  int n = con->send(data.c_str() + nwritten, data.length() - nwritten);
617
  if (n < 0) {
618
      LOGERR(("ExecCmd::send: send failed\n"));
619
      return -1;
620
  }
621
  nwritten += n;
622
    }
623
    return nwritten;
624
}
625
626
int ExecCmd::receive(string& data, int cnt)
627
{
628
    NetconCli *con = dynamic_cast<NetconCli *>(m_fromcmd.get());
629
    if (con == 0) {
630
  LOGERR(("ExecCmd::receive: inpipe is closed\n"));
631
  return -1;
632
    }
633
    const int BS = 4096;
634
    char buf[BS];
635
    int ntot = 0;
636
    do {
637
        int toread = cnt > 0 ? MIN(cnt - ntot, BS) : BS;
638
        int n = con->receive(buf, toread);
639
        if (n < 0) {
640
            LOGERR(("ExecCmd::receive: error\n"));
641
            return -1;
642
        } else if (n > 0) {
643
            ntot += n;
644
            data.append(buf, n);
645
        } else {
646
            LOGDEB(("ExecCmd::receive: got 0\n"));
647
            break;
648
        }
649
    } while (cnt > 0 && ntot < cnt);
650
    return ntot;
651
}
652
653
int ExecCmd::getline(string& data)
654
{
655
    NetconCli *con = dynamic_cast<NetconCli *>(m_fromcmd.get());
656
    if (con == 0) {
657
  LOGERR(("ExecCmd::receive: inpipe is closed\n"));
658
  return -1;
659
    }
660
    const int BS = 1024;
661
    char buf[BS];
662
    int n = con->getline(buf, BS);
663
    if (n < 0) {
664
  LOGERR(("ExecCmd::getline: error\n"));
665
    } else if (n > 0) {
666
  data.append(buf, n);
667
    } else {
668
  LOGDEB(("ExecCmd::getline: got 0\n"));
669
    }
670
    return n;
671
}
672
673
// Wait for command status and clean up all resources.
674
int ExecCmd::wait()
675
{
676
    ExecCmdRsrc e(this);
677
    int status = -1;
678
    if (!m_killRequest && m_pid > 0) {
679
  if (waitpid(m_pid, &status, 0) < 0) {
680
      LOGERR(("ExecCmd::waitpid: returned -1 errno %d\n", errno));
681
      status = -1;
682
  }
683
        LOGDEB(("ExecCmd::wait: got status 0x%x\n", status));
684
  m_pid = -1;
685
    }
686
    // Let the ExecCmdRsrc cleanup
687
    return status;
688
}
689
690
bool ExecCmd::maybereap(int *status)
691
{
692
    ExecCmdRsrc e(this);
693
    *status = -1;
694
695
    if (m_pid <= 0) {
696
  // Already waited for ??
697
  return true;
698
    }
699
700
    pid_t pid = waitpid(m_pid, status, WNOHANG);
701
    if (pid < 0) {
702
        LOGERR(("ExecCmd::maybereap: returned -1 errno %d\n", errno));
703
  m_pid = -1;
704
  return true;
705
    } else if (pid == 0) {
706
  LOGDEB1(("ExecCmd::maybereap: not exited yet\n"));
707
  e.inactivate();
708
  return false;
709
    } else {
710
        LOGDEB(("ExecCmd::maybereap: got status 0x%x\n", status));
711
  m_pid = -1;
712
  return true;
713
    }
714
}
715
716
// Static
717
bool ExecCmd::backtick(const std::vector<std::string> cmd, std::string& out)
718
{
719
    vector<string>::const_iterator it = cmd.begin();
720
    it++;
721
    vector<string> args(it, cmd.end());
722
    ExecCmd mexec;
723
    int status = mexec.doexec(*cmd.begin(), args, 0, &out);
724
    return status == 0;
725
}
726
727
/// ReExec class methods ///////////////////////////////////////////////////
728
ReExec::ReExec(int argc, char *args[])
729
{
730
    init(argc, args);
731
}
732
733
void ReExec::init(int argc, char *args[])
734
{
735
    for (int i = 0; i < argc; i++) {
736
  m_argv.push_back(args[i]);
737
    }
738
    m_cfd = open(".", 0);
739
    char *cd = getcwd(0, 0);
740
    if (cd) 
741
  m_curdir = cd;
742
    free(cd);
743
}
744
745
void ReExec::insertArgs(const vector<string>& args, int idx)
746
{
747
    vector<string>::iterator it, cit;
748
    unsigned int cmpoffset = (unsigned int)-1;
749
750
    if (idx == -1 || string::size_type(idx) >= m_argv.size()) {
751
  it = m_argv.end();
752
  if (m_argv.size() >= args.size()) {
753
      cmpoffset = m_argv.size() - args.size();
754
  }
755
    } else {
756
  it = m_argv.begin() + idx;
757
  if (idx + args.size() <= m_argv.size()) {
758
      cmpoffset = idx;
759
  }
760
    }
761
762
    // Check that the option is not already there
763
    if (cmpoffset != (unsigned int)-1) {
764
  bool allsame = true;
765
  for (unsigned int i = 0; i < args.size(); i++) {
766
      if (m_argv[cmpoffset + i] != args[i]) {
767
      allsame = false;
768
      break;
769
      }
770
  }
771
  if (allsame)
772
      return;
773
    }
774
775
    m_argv.insert(it, args.begin(), args.end());
776
}
777
778
void ReExec::removeArg(const string& arg)
779
{
780
    for (vector<string>::iterator it = m_argv.begin(); 
781
   it != m_argv.end(); it++) {
782
  if (*it == arg)
783
      it = m_argv.erase(it);
784
    }
785
}
786
787
// Reexecute myself, as close as possible to the initial exec
788
void ReExec::reexec()
789
{
790
791
#if 0
792
    char *cwd;
793
    cwd = getcwd(0,0);
794
    FILE *fp = stdout; //fopen("/tmp/exectrace", "w");
795
    if (fp) {
796
  fprintf(fp, "reexec: pwd: [%s] args: ", cwd?cwd:"getcwd failed");
797
  for (vector<string>::const_iterator it = m_argv.begin();
798
       it != m_argv.end(); it++) {
799
      fprintf(fp, "[%s] ", it->c_str());
800
  }
801
  fprintf(fp, "\n");
802
    }
803
#endif
804
805
    // Execute the atexit funcs
806
    while (!m_atexitfuncs.empty()) {
807
  (m_atexitfuncs.top())();
808
  m_atexitfuncs.pop();
809
    }
810
811
    // Try to get back to the initial working directory
812
    if (m_cfd < 0 || fchdir(m_cfd) < 0) {
813
  LOGINFO(("ReExec::reexec: fchdir failed, trying chdir\n"));
814
  if (!m_curdir.empty() && chdir(m_curdir.c_str())) {
815
      LOGERR(("ReExec::reexec: chdir failed\n"));
816
  }
817
    }
818
819
    // Close all descriptors except 0,1,2
820
    libclf_closefrom(3);
821
822
    // Allocate arg vector (1 more for final 0)
823
    typedef const char *Ccharp;
824
    Ccharp *argv;
825
    argv = (Ccharp *)malloc((m_argv.size()+1) * sizeof(char *));
826
    if (argv == 0) {
827
  LOGERR(("ExecCmd::doexec: malloc() failed. errno %d\n", errno));
828
  return;
829
    }
830
  
831
    // Fill up argv
832
    int i = 0;
833
    vector<string>::const_iterator it;
834
    for (it = m_argv.begin(); it != m_argv.end(); it++) {
835
  argv[i++] = it->c_str();
836
    }
837
    argv[i] = 0;
838
    execvp(m_argv[0].c_str(), (char *const*)argv);
839
}
840
841
842
////////////////////////////////////////////////////////////////////
843
#else // TEST
844
#include <stdio.h>
845
#include <stdlib.h>
846
#include <unistd.h>
847
#include <string.h>
848
849
#include <string>
850
#include <iostream>
851
#include <vector>
852
using namespace std;
853
854
#include "execmd.h"
855
856
static int     op_flags;
857
#define OPT_MOINS 0x1
858
#define OPT_b   0x4 
859
#define OPT_w     0x8
860
#define OPT_c     0x10
861
#define OPT_r     0x20
862
863
const char *data = "Une ligne de donnees\n";
864
class CancelExcept {};
865
class MEAdv : public ExecCmdAdvise {
866
public:
867
    ExecCmd *cmd;
868
    void newData(int cnt) {
869
  if (op_flags & OPT_c) {
870
      static int  callcnt;
871
      if (callcnt++ == 3) {
872
      throw CancelExcept();
873
      }
874
  }
875
  cerr << "newData(" << cnt << ")" << endl;
876
  //  CancelCheck::instance().setCancel();
877
  //  CancelCheck::instance().checkCancel();
878
  //  cmd->setCancel();
879
    }
880
};
881
882
class MEPv : public ExecCmdProvide {
883
public:
884
    FILE *m_fp;
885
    string *m_input;
886
    MEPv(string *i) 
887
  : m_input(i)
888
    {
889
  m_fp = fopen("/etc/group", "r");
890
    }
891
    ~MEPv() {
892
  if (m_fp)
893
      fclose(m_fp);
894
    }
895
    void newData() {
896
  char line[1024];
897
  if (m_fp && fgets(line, 1024, m_fp)) {
898
      m_input->assign((const char *)line);
899
  } else {
900
      m_input->erase();
901
  }
902
    }
903
};
904
905
906
static char *thisprog;
907
static char usage [] =
908
"trexecmd [-c|-r] cmd [arg1 arg2 ...]\n" 
909
" -c : test cancellation (ie: trexecmd -c sleep 1000)\n"
910
" -r : test reexec\n"
911
"trexecmd -w cmd : do the which thing\n"
912
;
913
static void Usage(void)
914
{
915
    fprintf(stderr, "%s: usage:\n%s", thisprog, usage);
916
    exit(1);
917
}
918
919
ReExec reexec;
920
921
int main(int argc, char *argv[])
922
{
923
    reexec.init(argc, argv);
924
925
    if (0) {
926
  vector<string> newargs;
927
  newargs.push_back("newarg");
928
  newargs.push_back("newarg1");
929
  newargs.push_back("newarg2");
930
  newargs.push_back("newarg3");
931
  newargs.push_back("newarg4");
932
  reexec.insertArgs(newargs, 2);
933
    }
934
935
    thisprog = argv[0];
936
    argc--; argv++;
937
938
    while (argc > 0 && **argv == '-') {
939
  (*argv)++;
940
  if (!(**argv))
941
      /* Cas du "adb - core" */
942
      Usage();
943
  while (**argv)
944
      switch (*(*argv)++) {
945
      case 'c':   op_flags |= OPT_c; break;
946
      case 'r':   op_flags |= OPT_r; break;
947
      case 'w':   op_flags |= OPT_w; break;
948
      default: Usage();   break;
949
      }
950
        argc--; argv++;
951
    }
952
953
    if (argc < 1)
954
  Usage();
955
956
    string cmd = *argv++; argc--;
957
    vector<string> l;
958
    while (argc > 0) {
959
  l.push_back(*argv++); argc--;
960
    }
961
    //DebugLog::getdbl()->setloglevel(DEBDEB1);
962
    //DebugLog::setfilename("stderr");
963
    signal(SIGPIPE, SIG_IGN);
964
965
    if (op_flags & OPT_r) {
966
  chdir("/");
967
        argv[0] = strdup("");
968
  sleep(1);
969
        reexec.reexec();
970
    }
971
972
    if (op_flags & OPT_w) {
973
  string path;
974
  if (ExecCmd::which(cmd, path)) {
975
      cout << path << endl;
976
      exit(0);
977
  } 
978
  exit(1);
979
    }
980
    ExecCmd mexec;
981
    MEAdv adv;
982
    adv.cmd = &mexec;
983
    mexec.setAdvise(&adv);
984
    mexec.setTimeout(5);
985
    mexec.setStderr("/tmp/trexecStderr");
986
    mexec.putenv("TESTVARIABLE1=TESTVALUE1");
987
    mexec.putenv("TESTVARIABLE2=TESTVALUE2");
988
    mexec.putenv("TESTVARIABLE3=TESTVALUE3");
989
990
    string input, output;
991
    //    input = data;
992
    string *ip = 0;
993
    ip = &input;
994
995
    MEPv  pv(&input);
996
    mexec.setProvide(&pv);
997
998
    int status = -1;
999
    try {
1000
  status = mexec.doexec(cmd, l, ip, &output);
1001
    } catch (CancelExcept) {
1002
  cerr << "CANCELLED" << endl;
1003
    }
1004
1005
    fprintf(stderr, "Status: 0x%x\n", status);
1006
    cout << output;
1007
    exit (status >> 8);
1008
}
1009
#endif // TEST