Merge branch 'master' of gitorious.org:opensuse/libzypp
[platform/upstream/libzypp.git] / zypp / ExternalProgram.h
1 /*---------------------------------------------------------------------\
2 |                          ____ _   __ __ ___                          |
3 |                         |__  / \ / / . \ . \                         |
4 |                           / / \ V /|  _/  _/                         |
5 |                          / /__ | | | | | |                           |
6 |                         /_____||_| |_| |_|                           |
7 |                                                                      |
8 \---------------------------------------------------------------------*/
9 /** \file zypp/ExternalProgram.h
10 */
11
12
13 #ifndef ZYPP_EXTERNALPROGRAM_H
14 #define ZYPP_EXTERNALPROGRAM_H
15
16 #include <map>
17 #include <string>
18 #include <vector>
19
20 #include "zypp/base/ExternalDataSource.h"
21 #include "zypp/Pathname.h"
22
23 namespace zypp {
24
25     /**
26      * @short Execute a program and give access to its io
27      * An object of this class encapsulates the execution of
28      * an external program. It starts the program using fork
29      * and some exec.. call, gives you access to the program's
30      * stdio and closes the program after use.
31      *
32      * \code
33      *
34      * const char* argv[] =
35      * {
36      *     "/usr/bin/foo,
37      *     "--option1",
38      *     "--option2",
39      *     NULL
40      * };
41      *
42      * ExternalProgram prog( argv,
43      *                        ExternalProgram::Discard_Stderr,
44      *                        false, -1, true);
45      * string line;
46      * for(line = prog.receiveLine();
47      *     ! line.empty();
48      *     line = prog.receiveLine() )
49      * {
50      *     stream << line;
51      * }
52      * prog.close();
53      *
54      * \endcode
55      */
56     class ExternalProgram : public zypp::externalprogram::ExternalDataSource
57     {
58
59     public:
60
61       typedef std::vector<std::string> Arguments;
62
63       /**
64        * Define symbols for different policies on the handling
65        * of stderr
66        */
67       enum Stderr_Disposition {
68         Normal_Stderr,
69         Discard_Stderr,
70         Stderr_To_Stdout,
71         Stderr_To_FileDesc
72       };
73
74
75       /**
76        * For passing additional environment variables to set
77        */
78       typedef std::map<std::string,std::string> Environment;
79
80       /**
81        * Start the external program by using the shell <tt>/bin/sh<tt>
82        * with the option <tt>-c</tt>. You can use io direction symbols < and >.
83        * @param commandline a shell commandline that is appended to
84        * <tt>/bin/sh -c</tt>.
85        * @param default_locale whether to set LC_ALL=C before starting
86        * @param root directory to chroot into, / or empty to not chroot
87        */
88       ExternalProgram (std::string commandline,
89                      Stderr_Disposition stderr_disp = Normal_Stderr,
90                      bool use_pty = false, int stderr_fd = -1, bool default_locale = false,
91                      const Pathname& root = "");
92
93       /**
94        * Start an external program by giving the arguments as an arry of char *pointers.
95        * If environment is provided, varaiables will be added to the childs environment,
96        * overwriting existing ones.
97        *
98        * Stdin redirection: If the \b 1st argument starts with a \b '<', the remaining
99        * part is treated as file opened for reading on standard input (or \c /dev/null
100        * if empty).
101        * \code
102        *   // cat file /tmp/x
103        *   const char* argv[] = { "</tmp/x", "cat", NULL };
104        *   ExternalProgram prog( argv );
105        * \endcode
106        */
107
108       ExternalProgram();
109
110       ExternalProgram (const Arguments &argv,
111                      Stderr_Disposition stderr_disp = Normal_Stderr,
112                      bool use_pty = false, int stderr_fd = -1, bool default_locale = false,
113                      const Pathname& root = "");
114
115       ExternalProgram (const Arguments &argv, const Environment & environment,
116                      Stderr_Disposition stderr_disp = Normal_Stderr,
117                      bool use_pty = false, int stderr_fd = -1, bool default_locale = false,
118                      const Pathname& root = "");
119
120       ExternalProgram (const char *const *argv,
121                      Stderr_Disposition stderr_disp = Normal_Stderr,
122                      bool use_pty = false, int stderr_fd = -1, bool default_locale = false,
123                      const Pathname& root = "");
124
125       ExternalProgram (const char *const *argv, const Environment & environment,
126                      Stderr_Disposition stderr_disp = Normal_Stderr,
127                      bool use_pty = false, int stderr_fd = -1, bool default_locale = false,
128                      const Pathname& root = "");
129
130       ExternalProgram (const char *binpath, const char *const *argv_1,
131                      bool use_pty = false);
132
133
134       ExternalProgram (const char *binpath, const char *const *argv_1, const Environment & environment,
135                      bool use_pty = false);
136
137
138       ~ExternalProgram();
139
140       /** Wait for the progamm to complete. */
141       int close();
142
143       /**
144        * Kill the program
145        */
146       bool kill();
147
148       /**
149        * Return whether program is running
150        */
151       bool running();
152
153       /**
154        * return pid
155        * */
156       pid_t getpid() { return pid; }
157
158       /** The command we're executing. */
159       const std::string & command() const
160       { return _command; }
161
162       /** Some detail telling why the execution failed, if it failed.
163        * Empty if the command is still running or successfully completed.
164        *
165        * \li <tt>Can't open pty (%s).</tt>
166        * \li <tt>Can't open pipe (%s).</tt>
167        * \li <tt>Can't fork (%s).</tt>
168        * \li <tt>Command exited with status %d.</tt>
169        * \li <tt>Command was killed by signal %d (%s).</tt>
170       */
171       const std::string & execError() const
172       { return _execError; }
173
174       /**
175        * origfd will be accessible as newfd and closed (unless they were equal)
176        */
177       static void renumber_fd (int origfd, int newfd);
178
179     public:
180
181       /**
182        * Redirect all command output to an \c ostream.
183        * Returns when the command has completed.
184        * \code
185        *   std::ostringstream s;
186        *   ExternalProgram("pwd") >> s;
187        *   SEC << s.str() << endl;
188        * \endcode
189        * \code
190        *   std::ostringstream s;
191        *   ExternalProgram prog("ls -l wrzl");
192        *   prog >> s;
193        *   if ( prog.close() == 0 )
194        *     MIL << s.str() << endl;
195        *   else
196        *     ERR << prog.execError() << endl;
197        * \endcode
198        */
199       std::ostream & operator>>( std::ostream & out_r );
200
201     protected:
202       int checkStatus( int );
203
204     private:
205
206       /**
207        * Set to true, if a pair of ttys is used for communication
208        * instead of a pair of pipes.
209        */
210       bool use_pty;
211
212       pid_t pid;
213       int _exitStatus;
214       /** Store the command we're executing. */
215       std::string _command;
216       /** Remember execution errors like failed fork/exec. */
217       std::string _execError;
218
219       void start_program (const char *const *argv, const Environment & environment,
220                         Stderr_Disposition stderr_disp = Normal_Stderr,
221                         int stderr_fd = -1, bool default_locale = false,
222                         const char* root = NULL);
223
224     };
225
226
227   namespace _ExternalProgram
228   {
229     /** Helper providing pipe FDs for \ref ExternalProgramWithStderr.
230      * Moved to a basse class because the pipe needs to be initialized
231      * before the \ref ExternalProgram base class is initialized.
232      * \see \ref ExternalProgramWithStderr
233      */
234     struct EarlyPipe
235     {
236       enum { R=0, W=1 };
237       EarlyPipe();
238       ~EarlyPipe();
239       void closeW()             { if ( _fds[W] != -1 ) { ::close( _fds[W] ); _fds[W] = -1; } }
240       FILE * stderr()           { return _stderr; }
241       protected:
242         FILE * _stderr;
243         int _fds[2];
244     };
245   }
246
247   /** ExternalProgram extended to offer reading programs stderr.
248    * \see \ref ExternalProgram
249    */
250   class ExternalProgramWithStderr : private _ExternalProgram::EarlyPipe, public ExternalProgram
251   {
252     public:
253       ExternalProgramWithStderr( const Arguments & argv_r )
254         : ExternalProgram( argv_r, Stderr_To_FileDesc, /*use_pty*/false, _fds[W] )
255       { _initStdErr(); }
256
257       ExternalProgramWithStderr( const Arguments & argv_r, const Environment & environment_r )
258         : ExternalProgram( argv_r, environment_r, Stderr_To_FileDesc, /*use_pty*/false, _fds[W] )
259       { _initStdErr(); }
260
261     public:
262       /** Return \c FILE* to read programms stderr (O_NONBLOCK set). */
263       _ExternalProgram::EarlyPipe::stderr;
264
265       /** Read data up to \c delim_r from stderr (nonblocking).
266        * \note If \c delim_r is '\0', we read as much data as possible.
267        * \return \c false if data are not yet available (\c retval_r remains untouched then).
268        */
269       bool stderrGetUpTo( std::string & retval_r, const char delim_r, bool returnDelim_r = false );
270
271       /** Read next complete line from stderr (nonblocking).
272        * \return \c false if data are not yet available (\c retval_r remains untouched then).
273        */
274       bool stderrGetline( std::string & retval_r, bool returnDelim_r = false  )
275       { return stderrGetUpTo( retval_r, '\n', returnDelim_r ); }
276
277     private:
278       /** Close write end of the pipe (childs end). */
279       void _initStdErr()
280       { closeW(); }
281
282     private:
283       std::string _buffer;
284   };
285
286 } // namespace zypp
287
288 #endif // ZYPP_EXTERNALPROGRAM_H