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