source: rtems-tools/rtemstoolkit/rld-process.cpp @ 87e0e76

4.104.11
Last change on this file since 87e0e76 was 87e0e76, checked in by Chris Johns <chrisj@…>, on Sep 13, 2014 at 2:09:16 AM

Refactor code into the RTEMS Toolkit.

  • Property mode set to 100644
File size: 12.9 KB
Line 
1/*
2 * Copyright (c) 2011, Chris Johns <chrisj@rtems.org>
3 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17#include "config.h"
18
19#include <ctype.h>
20#include <errno.h>
21#include <fcntl.h>
22#include <stdlib.h>
23#include <stdio.h>
24#include <string.h>
25#include <sys/stat.h>
26#include <unistd.h>
27
28#ifdef HAVE_SYS_WAIT_H
29#include <sys/wait.h>
30#endif
31
32#ifndef WIFEXITED
33#define WIFEXITED(S) (((S) & 0xff) == 0)
34#endif
35#ifndef WEXITSTATUS
36#define WEXITSTATUS(S) (((S) & 0xff00) >> 8)
37#endif
38#ifndef WIFSIGNALED
39#define WIFSIGNALED(S) (((S) & 0xff) != 0 && ((S) & 0xff) != 0x7f)
40#endif
41#ifndef WTERMSIG
42#define WTERMSIG(S) ((S) & 0x7f)
43#endif
44#ifndef WIFSTOPPED
45#define WIFSTOPPED WIFEXITED
46#endif
47#ifndef WSTOPSIG
48#define WSTOPSIG WEXITSTATUS
49#endif
50
51#if __WIN32__
52#define CREATE_MODE (S_IRUSR | S_IWUSR)
53#define OPEN_FLAGS  (O_BINARY)
54#else
55#define CREATE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH)
56#define OPEN_FLAGS  (0)
57#endif
58
59#include <iostream>
60
61#include "rld.h"
62#include "rld-process.h"
63
64#include <libiberty.h>
65
66namespace rld
67{
68  namespace process
69  {
70    /**
71     * Global keep of temporary files if true. Used to help debug a system.
72     */
73    bool keep_temporary_files = false;
74
75    /**
76     * The temporary files.
77     */
78    temporary_files temporaries;
79
80    temporary_files::temporary_files ()
81    {
82    }
83
84    temporary_files::~temporary_files ()
85    {
86      clean_up ();
87    }
88
89    const std::string
90    temporary_files::get (const std::string& suffix, bool keep)
91    {
92      char* temp = ::make_temp_file (suffix.c_str ());
93
94      if (!temp)
95        throw rld::error ("bad temp name", "temp-file");
96
97      const std::string name = rld::find_replace (temp,
98                                                  RLD_PATH_SEPARATOR_STR RLD_PATH_SEPARATOR_STR,
99                                                  RLD_PATH_SEPARATOR_STR);
100      tempfile_ref ref (name, keep);
101      tempfiles.push_back (ref);
102      return name;
103    }
104
105    void
106    temporary_files::unlink (const tempfile_ref& ref)
107    {
108      if (!keep_temporary_files && !ref.keep)
109        rld::path::unlink (ref.name);
110    }
111
112    void
113    temporary_files::erase (const std::string& name)
114    {
115      for (tempfile_container::iterator tfi = tempfiles.begin ();
116           tfi != tempfiles.end ();
117           ++tfi)
118      {
119        if ((*tfi).name == name)
120        {
121          unlink (*tfi);
122          tempfiles.erase (tfi);
123          break;
124        }
125      }
126    }
127
128    void
129    temporary_files::keep (const std::string& name)
130    {
131      for (tempfile_container::iterator tfi = tempfiles.begin ();
132           tfi != tempfiles.end ();
133           ++tfi)
134      {
135        if ((*tfi).name == name)
136        {
137          (*tfi).keep = true;
138          break;
139        }
140      }
141    }
142
143    void
144    temporary_files::clean_up ()
145    {
146      for (tempfile_container::iterator tfi = tempfiles.begin ();
147           tfi != tempfiles.end ();
148           ++tfi)
149      {
150        unlink (*tfi);
151      }
152    }
153
154    tempfile::tempfile (const std::string& suffix, bool _keep)
155      : suffix (suffix),
156        overridden (false),
157        fd (-1),
158        level (0)
159    {
160      _name = temporaries.get (suffix, _keep);
161    }
162
163    tempfile::~tempfile ()
164    {
165      close ();
166      temporaries.erase (_name);
167    }
168
169    void
170    tempfile::open (bool writable)
171    {
172      if (fd < 0)
173      {
174        bool ok = rld::path::check_file (_name);
175        int  flags = writable ? O_RDWR : O_RDONLY;
176        int  mode = writable ? CREATE_MODE : 0;
177
178        if (writable && overridden)
179        {
180          flags |= O_CREAT | O_TRUNC | O_APPEND;
181        }
182        else
183        {
184          if (!ok)
185            throw rld::error ("Not found.", "tempfile open:" + _name);
186        }
187
188        level = 0;
189        fd = ::open (_name.c_str (), flags, mode);
190        if (fd < 0)
191          throw rld::error (::strerror (errno), "tempfile open:" + _name);
192      }
193    }
194
195    void
196    tempfile::close ()
197    {
198      if (fd != -1)
199      {
200        ::close (fd);
201        fd = -1;
202        level = 0;
203      }
204    }
205
206    void
207    tempfile::override (const std::string& name_)
208    {
209      if (fd >= 0)
210        throw rld::error ("Already open", "tempfile override");
211      rld::path::unlink (_name);
212      overridden = true;
213      _name = name_ + suffix;
214    }
215
216    void
217    tempfile::keep ()
218    {
219      temporaries.keep (_name);
220    }
221
222    const std::string&
223    tempfile::name () const
224    {
225      return _name;
226    }
227
228    size_t
229    tempfile::size ()
230    {
231      if (fd < 0)
232        return 0;
233
234      struct stat sb;
235      if (::stat (_name.c_str (), &sb) == 0)
236        return sb.st_size;
237
238      return 0;
239    }
240
241    void
242    tempfile::read (std::string& all)
243    {
244      all.clear ();
245      if (fd != -1)
246      {
247        if (level)
248          all.append (buf, level);
249        level = 0;
250        while (true)
251        {
252          int read = ::read (fd, buf, sizeof (buf) );
253          if (read < 0)
254            throw rld::error (::strerror (errno), "tempfile get read:" + _name);
255          else if (read == 0)
256            break;
257          else
258            all.append (buf, read);
259        }
260      }
261    }
262
263    void
264    tempfile::read_line (std::string& line)
265    {
266      line.clear ();
267      if (fd != -1)
268      {
269        bool reading = true;
270        while (reading)
271        {
272          if (level < (sizeof (buf) - 1))
273          {
274            memset (buf + level, 0, sizeof (buf) - level);
275            int read = ::read (fd, buf + level, sizeof (buf) - level - 1);
276            if (read < 0)
277              throw rld::error (::strerror (errno), "tempfile read:" + _name);
278            else if (read == 0)
279              reading = false;
280            else
281              level += read;
282          }
283          if (level)
284          {
285            char* lf = ::strchr (buf, '\n');
286            int   len = level;
287            if (lf)
288              len = lf - &buf[0] + 1;
289            if (lf || !reading)
290            {
291              line.append (buf, len);
292              level -= len;
293            }
294            if (level)
295              ::memmove (buf, &buf[len], level + 1);
296            reading = false;
297          }
298        }
299      }
300    }
301
302    void
303    tempfile::write (const std::string& s)
304    {
305      const char* p = s.c_str ();
306      size_t      l = s.length ();
307      while (l)
308      {
309        int written = ::write (fd, p, l);
310        if (written < 0)
311            throw rld::error (::strerror (errno), "tempfile write:" + _name);
312        if (written == 0)
313          break;
314        l -= written;
315      }
316    }
317
318    void
319    tempfile::write_line (const std::string& s)
320    {
321      write (s);
322      write (RLD_LINE_SEPARATOR);
323    }
324
325    void
326    tempfile::write_lines (const rld::strings& ss)
327    {
328      for (rld::strings::const_iterator ssi = ss.begin ();
329           ssi != ss.end ();
330           ++ssi)
331      {
332        write_line (*ssi);
333      }
334    }
335
336    void
337    tempfile::output (std::ostream& out)
338    {
339      std::string prefix;
340      output (prefix, out);
341    }
342
343    void
344    tempfile::output (const std::string& prefix,
345                      std::ostream&      out,
346                      bool               line_numbers)
347    {
348      if (fd == -1)
349      {
350        std::string line;
351        int         lc = 0;
352        open ();
353        while (true)
354        {
355          read_line (line);
356          ++lc;
357          if (line.empty ())
358            break;
359          if (!prefix.empty ())
360            out << prefix << ": ";
361          if (line_numbers)
362            out << lc << ": ";
363          out << line << std::flush;
364        }
365        close ();
366      }
367    }
368
369    void
370    set_keep_temporary_files ()
371    {
372      keep_temporary_files = true;
373    }
374
375    void
376    temporaries_clean_up ()
377    {
378      temporaries.clean_up ();
379    }
380
381    void
382    args_append (arg_container& args, const std::string& str)
383    {
384      rld::strings ss;
385      rld::split (ss, str);
386      for (rld::strings::iterator ssi = ss.begin ();
387           ssi != ss.end ();
388           ++ssi)
389      {
390        args.push_back (*ssi);
391      }
392    }
393
394    status
395    execute (const std::string& pname,
396             const std::string& command,
397             const std::string& outname,
398             const std::string& errname)
399    {
400      arg_container args;
401      parse_command_line (command, args);
402      return execute (pname, args, outname, errname);
403    }
404
405    status
406    execute (const std::string&   pname,
407             const arg_container& args,
408             const std::string&   outname,
409             const std::string&   errname)
410    {
411      if (rld::verbose (RLD_VERBOSE_TRACE))
412      {
413        std::cout << "execute: ";
414        for (size_t a = 0; a < args.size (); ++a)
415          std::cout << args[a] << ' ';
416        std::cout << std::endl;
417      }
418
419      const char** cargs = new const char* [args.size () + 1];
420
421      for (size_t a = 0; a < args.size (); ++a)
422        cargs[a] = args[a].c_str ();
423      cargs[args.size ()] = 0;
424
425      int err = 0;
426      int s = 0;
427
428      const char* serr = pex_one (PEX_LAST | PEX_SEARCH,
429                                  args[0].c_str (),
430                                  (char* const*) cargs,
431                                  pname.c_str (),
432                                  outname.c_str (),
433                                  errname.c_str (),
434                                  &s,
435                                  &err);
436
437      delete [] cargs;
438
439      if (serr)
440        throw rld::error ("execute: " + args[0], serr);
441      else if (err)
442        throw rld::error ("execute: " + args[0], ::strerror (err));
443
444      status _status;
445
446      if (rld::verbose (RLD_VERBOSE_TRACE))
447        std::cout << "execute: status: ";
448
449      if (WIFEXITED (s))
450      {
451        _status.type = status::normal;
452        _status.code = WEXITSTATUS (s);
453        if (rld::verbose (RLD_VERBOSE_TRACE))
454          std::cout << _status.code << std::endl;
455      }
456      else if (WIFSIGNALED (s))
457      {
458        _status.type = status::signal;
459        _status.code = WTERMSIG (s);
460        if (rld::verbose (RLD_VERBOSE_TRACE))
461          std::cout << "signal: " << _status.code << std::endl;
462      }
463      else if (WIFSTOPPED (s))
464      {
465        _status.type = status::stopped;
466        _status.code = WSTOPSIG (s);
467        if (rld::verbose (RLD_VERBOSE_TRACE))
468          std::cout << "stopped: " << _status.code << std::endl;
469      }
470      else
471        throw rld::error ("execute: " + args[0], "unknown status returned");
472
473      return _status;
474    }
475
476    /*
477     * The code is based on this C file:
478     *  http://cybertiggyr.com/pcm/src/parse.c
479     */
480    void
481    parse_command_line (const std::string& command, arg_container& args)
482    {
483      enum pstate
484      {
485        pstate_discard_space,
486        pstate_accumulate_quoted,
487        pstate_accumulate_raw
488      };
489
490      args.clear ();
491
492      const char quote = '"';
493      const char escape = '\\';
494      pstate     state = pstate_discard_space;
495      size_t     start = 0;
496      size_t     i = 0;
497
498      while (i < command.size ())
499      {
500        switch (state)
501        {
502          case pstate_discard_space:
503            if (command[i] == quote)
504            {
505              ++i;
506              start = i;
507              state = pstate_accumulate_quoted;
508            }
509            else if (::isspace (command[i]))
510            {
511              ++i;
512            }
513            else /* includes escape */
514            {
515              start = i;
516              state = pstate_accumulate_raw;
517            }
518            break;
519
520          case pstate_accumulate_quoted:
521            if (command[i] == quote)
522            {
523              args.push_back (command.substr (start, i - 1));
524              ++i;
525              state = pstate_discard_space;
526            }
527            else if ((command[i] == escape) && (command[i + 1] == quote))
528            {
529              i += 2;
530            }
531            else /* includes space */
532            {
533              ++i;
534            }
535            break;
536
537          case pstate_accumulate_raw:
538            if (command[i] == quote)
539            {
540              throw rld::error ("quote in token", "command parse");
541            }
542            else if ((command[i] == escape) && (command[i + 1] == quote))
543            {
544              i += 2;
545            }
546            else if (::isspace (command[i]))
547            {
548              args.push_back (command.substr (start, i - 1));
549              ++i;
550              state = pstate_discard_space;
551            }
552            else
553            {
554              ++i;
555            }
556            break;
557        }
558      }
559    }
560  }
561}
Note: See TracBrowser for help on using the repository browser.