source: rtems-tools/linkers/rld-process.cpp @ 32cd4fc

4.104.115
Last change on this file since 32cd4fc was 32cd4fc, checked in by Chris Johns <chrisj@…>, on 09/07/14 at 00:47:00

rtems-tld: Add --wrapper option to aid testing.

The --wrapper option lets a user control the wrapper file name and
location to aid testing.

Add keep support to tempfiles so specific tempfile can be set
to be kept.

Add unlink to the rld::path namespace.

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