Switch to unified view

a/src/execmd.h b/src/execmd.h
...
...
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 _EXECMD_H_INCLUDED_
17
#ifndef _EXECMD_H_INCLUDED_
18
#define _EXECMD_H_INCLUDED_
18
#define _EXECMD_H_INCLUDED_
19
19
20
#include <signal.h>
21
22
#include <string>
20
#include <string>
23
#include <vector>
21
#include <vector>
24
#include <stack>
22
#include <stack>
25
23
26
#include "netcon.h"
27
28
/** 
24
/**
29
 * Callback function object to advise of new data arrival, or just periodic 
25
 * Callback function object to advise of new data arrival, or just periodic
30
 * heartbeat if cnt is 0. 
26
 * heartbeat if cnt is 0.
31
 *
27
 *
32
 * To interrupt the command, the code using ExecCmd should either
28
 * To interrupt the command, the code using ExecCmd should either
33
 * raise an exception inside newData() (and catch it in doexec's caller), or 
29
 * raise an exception inside newData() (and catch it in doexec's caller), or
34
 * call ExecCmd::setKill() 
30
 * call ExecCmd::setKill()
35
 * 
31
 *
36
 */
32
 */
37
class ExecCmdAdvise {
33
class ExecCmdAdvise {
38
 public:
34
public:
39
    virtual ~ExecCmdAdvise() {}
35
    virtual ~ExecCmdAdvise() {}
40
    virtual void newData(int cnt) = 0;
36
    virtual void newData(int cnt) = 0;
41
};
37
};
42
38
43
/** 
39
/**
44
 * Callback function object to get more input data. Data has to be provided
40
 * Callback function object to get more input data. Data has to be provided
45
 * in the initial input string, set it to empty to signify eof.
41
 * into the initial input string, set it to empty to signify eof.
46
 */
42
 */
47
class ExecCmdProvide {
43
class ExecCmdProvide {
48
 public:
44
public:
49
    virtual ~ExecCmdProvide() {}
45
    virtual ~ExecCmdProvide() {}
50
    virtual void newData() = 0;
46
    virtual void newData() = 0;
51
};
47
};
52
48
53
/**
49
/**
54
 * Execute command possibly taking both input and output (will do
50
 * Execute command possibly taking both input and output (will do
55
 * asynchronous io as appropriate for things to work).
51
 * asynchronous io as appropriate for things to work).
56
 *
52
 *
57
 * Input to the command can be provided either once in a parameter to doexec
53
 * Input to the command can be provided either once in a parameter to doexec
58
 * or provided in chunks by setting a callback which will be called to 
54
 * or provided in chunks by setting a callback which will be called to
59
 * request new data. In this case, the 'input' parameter to doexec may be
55
 * request new data. In this case, the 'input' parameter to doexec may be
60
 * empty (but not null)
56
 * empty (but not null)
61
 *
57
 *
62
 * Output from the command is normally returned in a single string, but a
58
 * Output from the command is normally returned in a single string, but a
63
 * callback can be set to be called whenever new data arrives, in which case
59
 * callback can be set to be called whenever new data arrives, in which case
64
 * it is permissible to consume the data and erase the string.
60
 * it is permissible to consume the data and erase the string.
65
 * 
61
 *
66
 * Note that SIGPIPE should be ignored and SIGCLD blocked when calling doexec,
62
 * Note that SIGPIPE should be ignored and SIGCLD blocked when calling doexec,
67
 * else things might fail randomly. (This is not done inside the class because
63
 * else things might fail randomly. (This is not done inside the class because
68
 * of concerns with multithreaded programs).
64
 * of concerns with multithreaded programs).
69
 *
65
 *
70
 */
66
 */
71
class ExecCmd {
67
class ExecCmd {
72
 public:
68
public:
73
    // Use vfork instead of fork. This must not be called in a multithreaded 
69
    // Use vfork instead of fork. Our vfork usage is multithread-compatible as
74
    // program.
70
    // far as I can see, but just in case...
75
    static void useVfork(bool on)
71
    static void useVfork(bool on);
76
    {
72
77
  o_useVfork  = on;
78
    }
79
    /** 
73
    /**
80
     * Add/replace environment variable before executing command. This must
74
     * Add/replace environment variable before executing command. This must
81
     * be called before doexec() to have an effect (possibly multiple
75
     * be called before doexec() to have an effect (possibly multiple
82
     * times for several variables).
76
     * times for several variables).
83
     * @param envassign an environment assignment string ("name=value")
77
     * @param envassign an environment assignment string ("name=value")
84
     */
78
     */
85
    void putenv(const std::string &envassign);
79
    void putenv(const std::string& envassign);
86
    void putenv(const std::string &name, const std::string& value);
80
    void putenv(const std::string& name, const std::string& value);
87
81
88
    /** 
82
    /**
83
     * Try to set a limit on child process vm size. This will use
84
     * setrlimit() and RLIMIT_AS/VMEM if available. Parameter is in
85
     * units of 2**10. Must be called before starting the command, default
86
     * is inherit from parent.
87
     */
88
    void setrlimit_as(int mbytes);
89
90
    /**
89
     * Set function objects to call whenever new data is available or on
91
     * Set function objects to call whenever new data is available or on
90
     * select timeout / whenever new data is needed to send. Must be called
92
     * select timeout. The data itself is stored in the output string.
91
     * before doexec()
93
     * Must be set before calling doexec.
92
     */
94
     */
93
    void setAdvise(ExecCmdAdvise *adv) {m_advise = adv;}
95
    void setAdvise(ExecCmdAdvise *adv);
94
    void setProvide(ExecCmdProvide *p) {m_provide = p;}
95
96
    /**
96
    /*
97
     * Set function object to call whenever new data is needed. The
98
     * data should be stored in the input string. Must be set before
99
     * calling doexec()
100
     */
101
    void setProvide(ExecCmdProvide *p);
102
103
    /**
97
     * Set select timeout in milliseconds. The default is 1 S. 
104
     * Set select timeout in milliseconds. The default is 1 S.
98
     * This is NOT a time after which an error will occur, but the period of
105
     * This is NOT a time after which an error will occur, but the period of
99
     * the calls to the cancellation check routine.
106
     * the calls to the advise routine (which normally checks for cancellation).
100
     */
107
     */
101
    void setTimeout(int mS) {if (mS > 30) m_timeoutMs = mS;}
108
    void setTimeout(int mS);
102
109
103
    /** 
110
    /**
104
     * Set destination for stderr data. The default is to let it alone (will 
111
     * Set destination for stderr data. The default is to let it alone (will
105
     * usually go to the terminal or to wherever the desktop messages go).
112
     * usually go to the terminal or to wherever the desktop messages go).
106
     * There is currently no option to put stderr data into a program variable
113
     * There is currently no option to put stderr data into a program variable
107
     * If the parameter can't be opened for writing, the command's
114
     * If the parameter can't be opened for writing, the command's
108
     * stderr will be closed.
115
     * stderr will be closed.
109
     */
116
     */
110
    void setStderr(const std::string &stderrFile) {m_stderrFile = stderrFile;}
117
    void setStderr(const std::string& stderrFile);
111
118
112
    /**
119
    /**
113
     * Execute command. 
120
     * Execute command.
114
     *
121
     *
115
     * Both input and output can be specified, and asynchronous 
122
     * Both input and output can be specified, and asynchronous
116
     * io (select-based) is used to prevent blocking. This will not
123
     * io (select-based) is used to prevent blocking. This will not
117
     * work if input and output need to be synchronized (ie: Q/A), but
124
     * work if input and output need to be synchronized (ie: Q/A), but
118
     * works ok for filtering.
125
     * works ok for filtering.
119
     * The function is exception-safe. In case an exception occurs in the 
126
     * The function is exception-safe. In case an exception occurs in the
120
     * advise callback, fds and pids will be cleaned-up properly.
127
     * advise callback, fds and pids will be cleaned-up properly.
121
     *
128
     *
122
     * @param cmd the program to execute. This must be an absolute file name 
129
     * @param cmd the program to execute. This must be an absolute file name
123
     *   or exist in the PATH.
130
     *   or exist in the PATH.
124
     * @param args the argument vector (NOT including argv[0]).
131
     * @param args the argument vector (NOT including argv[0]).
125
     * @param input Input to send TO the command.
132
     * @param input Input to send TO the command.
126
     * @param output Output FROM the command.
133
     * @param output Output FROM the command.
127
     * @return the exec ouput status (0 if ok), or -1
134
     * @return the exec output status (0 if ok), or -1
128
     */
135
     */
129
    int doexec(const std::string &cmd, const std::vector<std::string>& args, 
136
    int doexec(const std::string& cmd, const std::vector<std::string>& args,
130
         const std::string *input = 0, 
137
               const std::string *input = 0,
131
         std::string *output = 0);
138
               std::string *output = 0);
132
139
133
    /** Same as doexec but cmd and args in one vector */
140
    /** Same as doexec but cmd and args in one vector */
134
    int doexec1(const std::vector<std::string>& args, 
141
    int doexec1(const std::vector<std::string>& args,
135
                const std::string *input = 0, 
142
                const std::string *input = 0,
136
                std::string *output = 0) {
143
                std::string *output = 0) {
137
        if (args.empty())
144
        if (args.empty()) {
138
            return -1;
145
            return -1;
146
        }
139
        return doexec(args[0],
147
        return doexec(args[0],
140
                      std::vector<std::string>(args.begin() + 1, args.end()),
148
                      std::vector<std::string>(args.begin() + 1, args.end()),
141
                      input, output);
149
                      input, output);
142
    }
150
    }
143
151
144
    /*
152
    /*
145
     * The next four methods can be used when a Q/A dialog needs to be 
153
     * The next four methods can be used when a Q/A dialog needs to be
146
     * performed with the command
154
     * performed with the command
147
     */
155
     */
148
    int startExec(const std::string &cmd, const std::vector<std::string>& args, 
156
    int startExec(const std::string& cmd, const std::vector<std::string>& args,
149
        bool has_input, bool has_output);
157
                  bool has_input, bool has_output);
150
    int send(const std::string& data);
158
    int send(const std::string& data);
151
    int receive(std::string& data, int cnt = -1);
159
    int receive(std::string& data, int cnt = -1);
160
161
    /** Read line. Will call back periodically to check for cancellation */
162
    int getline(std::string& data);
163
164
    /** Read line. Timeout after timeosecs seconds */
152
    int getline(std::string& data, int timeosecs = -1);
165
    int getline(std::string& data, int timeosecs);
166
153
    int wait();
167
    int wait();
154
    /** Wait with WNOHANG set. 
168
    /** Wait with WNOHANG set.
155
  @return true if process exited, false else.
169
    @return true if process exited, false else.
156
  @param O: status, the wait(2) call's status value */
170
    @param O: status, the wait(2) call's status value */
157
    bool maybereap(int *status);
171
    bool maybereap(int *status);
158
172
159
    pid_t getChildPid() {return m_pid;}
173
    pid_t getChildPid();
160
174
161
    /** 
175
    /**
162
     * Cancel/kill command. This can be called from another thread or
176
     * Cancel/kill command. This can be called from another thread or
163
     * from the advise callback, which could also raise an exception to 
177
     * from the advise callback, which could also raise an exception
164
     * accomplish the same thing
178
     * to accomplish the same thing. In the owner thread, any I/O loop
165
     */
179
     * will exit at the next iteration, and the process will be waited for.
166
    void setKill() {m_killRequest = true;}
167
168
    /**
180
     */
181
    void setKill();
182
183
    /**
169
     * Get rid of current process (become ready for start). 
184
     * Get rid of current process (become ready for start). This will signal
185
     * politely the process to stop, wait a moment, then terminate it. This
186
     * is a blocking call.
187
     */
188
    void zapChild();
189
170
     */
190
    /**
171
    void zapChild() {setKill(); (void)wait();}
191
     * Request process termination (SIGTERM or equivalent). This returns
192
     * immediately
193
     */
194
    bool requestChildExit();
172
195
173
    ExecCmd()
196
    enum ExFlags {EXF_NONE,
174
  : m_advise(0), m_provide(0), m_timeoutMs(1000)
197
                  // Only does anything on windows. Used when starting
175
    {
198
                  // a viewer. The default is to hide the window,
176
  reset();
199
                  // because it avoids windows appearing and
177
    }
200
                  // disappearing when executing stuff for previewing
201
                  EXF_SHOWWINDOW = 1,
202
                 };
203
    ExecCmd(int flags = 0);
178
    ~ExecCmd();
204
    ~ExecCmd();
179
205
180
    /**
206
    /**
181
     * Utility routine: check if/where a command is found according to the
207
     * Utility routine: check if/where a command is found according to the
182
     * current PATH (or the specified one
208
     * current PATH (or the specified one
...
...
193
     * @param out output: what the command printed
219
     * @param out output: what the command printed
194
     * @return true if exec status was 0
220
     * @return true if exec status was 0
195
     */
221
     */
196
    static bool backtick(const std::vector<std::string> cmd, std::string& out);
222
    static bool backtick(const std::vector<std::string> cmd, std::string& out);
197
223
198
    friend class ExecCmdRsrc;
224
    class Internal;
199
 private:
225
private:
200
    static bool      o_useVfork;
226
    Internal *m;
201
202
    std::vector<std::string>   m_env;
203
    ExecCmdAdvise   *m_advise;
204
    ExecCmdProvide  *m_provide;
205
    bool             m_killRequest;
206
    int              m_timeoutMs;
207
    std::string           m_stderrFile;
208
    // Pipe for data going to the command
209
    int              m_pipein[2];
210
    NetconP          m_tocmd;
211
    // Pipe for data coming out
212
    int              m_pipeout[2];
213
    NetconP          m_fromcmd;
214
    // Subprocess id
215
    pid_t            m_pid;
216
    // Saved sigmask
217
    sigset_t         m_blkcld;
218
219
    // Reset internal state indicators. Any resources should have been
220
    // previously freed
221
    void reset() {
222
  m_killRequest = false;
223
  m_pipein[0] = m_pipein[1] = m_pipeout[0] = m_pipeout[1] = -1;
224
  m_pid = -1;
225
  sigemptyset(&m_blkcld);
226
    }
227
    // Child process code
228
    inline void dochild(const std::string &cmd, const char **argv, 
229
          const char **envv, bool has_input, bool has_output);
230
    /* Copyconst and assignment private and forbidden */
227
    /* Copyconst and assignment are private and forbidden */
231
    ExecCmd(const ExecCmd &) {}
228
    ExecCmd(const ExecCmd&) {}
232
    ExecCmd& operator=(const ExecCmd &) {return *this;};
229
    ExecCmd& operator=(const ExecCmd&) {
230
        return *this;
231
    };
233
};
232
};
234
233
235
234
236
/** 
235
/**
237
 * Rexecute self process with the same arguments. 
236
 * Rexecute self process with the same arguments.
238
 *
237
 *
239
 * Note that there are some limitations:
238
 * Note that there are some limitations:
240
 *  - argv[0] has to be valid: an executable name which will be found in 
239
 *  - argv[0] has to be valid: an executable name which will be found in
241
 *    the path when exec is called in the initial working directory. This is 
240
 *    the path when exec is called in the initial working directory. This is
242
 *    by no means guaranteed. The shells do this, but argv[0] could be an
241
 *    by no means guaranteed. The shells do this, but argv[0] could be an
243
 *    arbitrary string.
242
 *    arbitrary string.
244
 *  - The initial working directory must be found and remain valid.
243
 *  - The initial working directory must be found and remain valid.
245
 *  - We don't try to do anything with fd 0,1,2. If they were changed by the
244
 *  - We don't try to do anything with fd 0,1,2. If they were changed by the
246
 *    program, their initial meaning won't be the same as at the moment of the 
245
 *    program, their initial meaning won't be the same as at the moment of the
247
 *    initial invocation.
246
 *    initial invocation.
248
 *  - We don't restore the signals. Signals set to be blocked 
247
 *  - We don't restore the signals. Signals set to be blocked
249
 *    or ignored by the program will remain ignored even if this was not their
248
 *    or ignored by the program will remain ignored even if this was not their
250
 *    initial state.
249
 *    initial state.
251
 *  - The environment is also not restored.
250
 *  - The environment is also not restored.
252
 *  - Others system aspects ?
251
 *  - Others system aspects ?
253
 *  - Other program state: application-dependant. Any external cleanup 
252
 *  - Other program state: application-dependant. Any external cleanup
254
 *    (temp files etc.) must be performed by the application. ReExec() 
253
 *    (temp files etc.) must be performed by the application. ReExec()
255
 *    duplicates the atexit() function to make this easier, but the 
254
 *    duplicates the atexit() function to make this easier, but the
256
 *    ReExec().atexit() calls must be done explicitely, this is not automatic
255
 *    ReExec().atexit() calls must be done explicitely, this is not automatic
257
 * 
256
 *
258
 * In short, this is usable in reasonably controlled situations and if there 
257
 * In short, this is usable in reasonably controlled situations and if there
259
 * are no security issues involved, but this does not perform miracles.
258
 * are no security issues involved, but this does not perform miracles.
260
 */
259
 */
261
class ReExec {
260
class ReExec {
262
public:
261
public:
263
    ReExec() {}
262
    ReExec() {}
264
    ReExec(int argc, char *argv[]);
263
    ReExec(int argc, char *argv[]);
265
    void init(int argc, char *argv[]);
264
    void init(int argc, char *argv[]);
266
    int atexit(void (*function)(void))
265
    int atexit(void (*function)(void)) {
267
    {
268
  m_atexitfuncs.push(function);
266
        m_atexitfuncs.push(function);
269
  return 0;
267
        return 0;
270
    }
268
    }
271
    void reexec();
269
    void reexec();
272
    const std::string& getreason() {return m_reason;}
270
    const std::string& getreason() {
271
        return m_reason;
272
    }
273
    // Insert new args into the initial argv. idx designates the place
273
    // Insert new args into the initial argv. idx designates the place
274
    // before which the new args are inserted (the default of 1
274
    // before which the new args are inserted (the default of 1
275
    // inserts after argv[0] which would probably be an appropriate
275
    // inserts after argv[0] which would probably be an appropriate
276
    // place for additional options)
276
    // place for additional options)
277
    void insertArgs(const std::vector<std::string>& args, int idx = 1);
277
    void insertArgs(const std::vector<std::string>& args, int idx = 1);