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

4.104.115
Last change on this file since a136346 was a136346, checked in by Chris Johns <chrisj@…>, on 08/05/14 at 13:01:15

Fix temporary file handling and add tempfile write support.

Move the static objects into the rld-process file and change the
clean up to a call.

Add support to write to tempfiles.

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