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