source: trunk/debathena/third/schroot/sbuild/sbuild-util.cc @ 24314

Revision 24314, 14.3 KB checked in by geofft, 14 years ago (diff)
In schroot: * Merge with Debian unstable; remaining changes: - Backport to Karmic, and adjust build-deps.
Line 
1/* Copyright © 2005-2007  Roger Leigh <rleigh@debian.org>
2 *
3 * schroot is free software: you can redistribute it and/or modify it
4 * under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 3 of the License, or
6 * (at your option) any later version.
7 *
8 * schroot is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 * General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program.  If not, see
15 * <http://www.gnu.org/licenses/>.
16 *
17 *********************************************************************/
18
19#include <config.h>
20
21#include "sbuild-chroot.h"
22#include "sbuild-error.h"
23#include "sbuild-util.h"
24
25#include <cerrno>
26#include <cstring>
27
28#include <sys/types.h>
29#include <sys/stat.h>
30#include <unistd.h>
31
32#ifdef HAVE_UUID
33#include <uuid/uuid.h>
34#else
35#include <time.h>
36#endif
37
38#include <boost/filesystem/convenience.hpp>
39
40using namespace sbuild;
41
42namespace
43{
44
45  /**
46   * Remove duplicate adjacent characters from a string.
47   *
48   * @param str the string to check.
49   * @param dup the duplicate character to check for.
50   * @returns a string with any duplicates removed.
51   */
52  std::string remove_duplicates (std::string const& str,
53                                 char               dup)
54  {
55    std::string ret;
56
57    for (std::string::size_type pos = 0;
58         pos < str.length();
59         ++pos)
60      {
61        ret += str[pos];
62        if (str[pos] == dup)
63          {
64            while (pos + 1 < str.length() &&
65                   str[pos + 1] == dup)
66              ++pos;
67          }
68      }
69
70    return ret;
71  }
72
73  typedef std::pair<sbuild::stat::error_code,const char *> emap;
74
75  /**
76   * This is a list of the supported error codes.  It's used to
77   * construct the real error codes map.
78   */
79  emap init_errors[] =
80    {
81      emap(sbuild::stat::FILE, N_("Failed to stat file")),
82      emap(sbuild::stat::FD,   N_("Failed to stat file descriptor"))
83    };
84
85  bool chroot_alphasort (sbuild::chroot::ptr const& c1,
86                         sbuild::chroot::ptr const& c2)
87  {
88    return c1->get_name() < c2->get_name();
89  }
90
91}
92
93template<>
94error<sbuild::stat::error_code>::map_type
95error<sbuild::stat::error_code>::error_strings
96(init_errors,
97 init_errors + (sizeof(init_errors) / sizeof(init_errors[0])));
98
99std::string
100sbuild::basename (std::string name)
101{
102  const char separator = '/';
103
104  // Remove trailing separators
105  std::string::size_type cur = name.length();
106  while (cur - 1 != 0 && name[cur - 1] == separator)
107    --cur;
108  name.resize(cur);
109
110  // Find last separator
111  std::string::size_type pos = name.rfind(separator);
112
113  std::string ret;
114  if (pos == std::string::npos)
115    ret = name; // No separators
116  else if (pos == 0 && name.length() == 1 && name[0] == separator)
117    ret = separator; // Only separators
118  else
119    ret = name.substr(pos + 1); // Basename only
120
121  return remove_duplicates(ret, separator);
122}
123
124std::string
125sbuild::dirname (std::string name)
126{
127  const char separator = '/';
128
129  // Remove trailing separators
130  std::string::size_type cur = name.length();
131  while (cur - 1 != 0 && name[cur - 1] == separator)
132    --cur;
133  name.resize(cur);
134
135  // Find last separator
136  std::string::size_type pos = name.rfind(separator);
137
138  std::string ret;
139  if (pos == std::string::npos)
140    ret = "."; // No directory components
141  else if (pos == 0)
142    ret = separator;
143  else
144    ret = name.substr(0, pos); // Dirname part
145
146  return remove_duplicates(ret, separator);
147}
148
149std::string
150sbuild::normalname (std::string name)
151{
152  const char separator = '/';
153
154  // Remove trailing separators
155  std::string::size_type cur = name.length();
156  while (cur - 1 != 0 && name[cur - 1] == separator)
157    --cur;
158  name.resize(cur);
159
160  return remove_duplicates(name, separator);
161}
162
163bool
164sbuild::is_absname (std::string const& name)
165{
166  if (name.empty() || name[0] != '/')
167    return false;
168  else
169    return true;
170}
171
172bool
173sbuild::is_valid_filename (std::string const& name,
174                           bool               lsb_mode)
175{
176  bool match = false;
177
178  if (lsb_mode)
179    {
180      static regex lanana_namespace("^[a-z0-9]+$");
181      static regex lsb_namespace("^_?([a-z0-9_.]+-)+[a-z0-9]+$");
182      static regex debian_cron_namespace("^[a-z0-9][a-z0-9-]*$");
183      static regex debian_dpkg_conffile_cruft("dpkg-(old|dist|new|tmp)$");
184
185      if ((regex_search(name, lanana_namespace) ||
186           regex_search(name, lsb_namespace) ||
187           regex_search(name, debian_cron_namespace)) &&
188          !regex_search(name, debian_dpkg_conffile_cruft))
189        match = true;
190    }
191  else
192    {
193      static regex traditional_namespace("^[a-zA-Z0-9_-]$");
194      if (regex_search(name, traditional_namespace))
195        match = true;
196    }
197
198  return match;
199}
200
201std::string
202sbuild::getcwd ()
203{
204  std::string cwd;
205
206  char *raw_cwd = ::getcwd (0, 0);
207  if (raw_cwd)
208    cwd = raw_cwd;
209  else
210    cwd = "/";
211  free(raw_cwd);
212
213  return cwd;
214}
215
216std::string
217sbuild::unique_identifier ()
218{
219  std::ostringstream id;
220  id.imbue(std::locale::classic());
221
222#ifdef HAVE_UUID
223  uuid_t uuid;
224  char uuid_str[37];
225  uuid_generate(uuid);
226  uuid_unparse(uuid, uuid_str);
227  uuid_clear(uuid);
228
229  id << uuid_str;
230#else
231  id << isodate(time(0)) << '-' << getpid();
232#endif
233
234  return id.str();
235}
236
237std::string
238sbuild::string_list_to_string (sbuild::string_list const& list,
239                               std::string const&         separator)
240{
241  std::string ret;
242
243  for (string_list::const_iterator cur = list.begin();
244       cur != list.end();
245       ++cur)
246    {
247      ret += *cur;
248      if (cur + 1 != list.end())
249        ret += separator;
250    }
251
252  return ret;
253}
254
255string_list
256sbuild::split_string (std::string const& value,
257                      std::string const& separator)
258{
259  string_list ret;
260
261  // Skip any separators at the start
262  std::string::size_type last_pos =
263    value.find_first_not_of(separator, 0);
264  // Find first separator.
265  std::string::size_type pos = value.find_first_of(separator, last_pos);
266
267  while (pos !=std::string::npos || last_pos != std::string::npos)
268    {
269      // Add to list
270      if (pos == std::string::npos)
271        // Entire string from last_pos
272        ret.push_back(value.substr(last_pos, pos));
273      else
274        // Between pos and last_pos
275        ret.push_back(value.substr(last_pos, pos - last_pos));
276
277      // Find next
278      last_pos = value.find_first_not_of(separator, pos);
279      pos = value.find_first_of(separator, last_pos);
280    }
281
282  return ret;
283}
284
285string_list
286sbuild::split_string_strict (std::string const& value,
287                             std::string const& separator)
288{
289  string_list ret;
290
291  std::string::size_type last_pos = 0;
292  // Find first separator.
293  std::string::size_type pos = value.find_first_of(separator, last_pos);
294
295  while (pos !=std::string::npos)
296    {
297      // Add to list
298      if (pos == std::string::npos)
299        // Entire string from last_pos
300        ret.push_back(value.substr(last_pos, pos));
301      else
302        // Between pos and last_pos
303        ret.push_back(value.substr(last_pos, pos - last_pos));
304
305      // Find next
306      last_pos = pos + separator.length();
307      pos = value.find_first_of(separator, last_pos);
308    }
309
310  return ret;
311}
312
313std::wstring
314sbuild::widen_string (std::string const& str,
315                      std::locale        locale)
316{
317  typedef std::codecvt<wchar_t, char, mbstate_t> codecvt_type;
318  codecvt_type const& cvt = std::use_facet<codecvt_type>(locale);
319  mbstate_t state;
320  const char *cbegin = str.data(), *cend = str.data() + str.size(), *cnext;
321  wchar_t *wcnext;
322  wchar_t wcbuf[80];
323  std::wstring ret;
324
325  std::memset(&state, 0, sizeof(mbstate_t));
326
327  while (1)
328    {
329      std::codecvt_base::result res =
330        cvt.in(state,
331               cbegin, cend, cnext,
332               wcbuf, wcbuf + (sizeof(wcbuf) / sizeof(wcbuf[0])), wcnext);
333
334      if (res == std::codecvt_base::ok || res == std::codecvt_base::partial)
335        {
336          ret += std::wstring(wcbuf, wcnext);
337          if (cend == cnext)
338            break;
339        }
340      else if (res == std::codecvt_base::noconv)
341        {
342          ret += std::wstring(cbegin, cend);
343          break;
344        }
345      else if (res == std::codecvt_base::error)
346        {
347          throw std::runtime_error
348            ("A character set conversion failed.  Please report this bug.");
349          break;
350        }
351      else
352        break;
353
354      cbegin = cnext;
355    }
356
357  return ret;
358}
359
360std::string
361sbuild::narrow_string (std::wstring const& str,
362                       std::locale         locale)
363{
364  typedef std::codecvt<wchar_t, char, mbstate_t> codecvt_type;
365  codecvt_type const& cvt = std::use_facet<codecvt_type>(locale);
366  mbstate_t state;
367  const wchar_t *wcbegin = str.data(), *wcend = str.data() + str.size(), *wcnext;
368  char *cnext;
369  char cbuf[80];
370  std::string ret;
371
372  std::memset(&state, 0, sizeof(mbstate_t));
373
374  while (1)
375    {
376      std::codecvt_base::result res =
377        cvt.out(state,
378                wcbegin, wcend, wcnext,
379                cbuf, cbuf + (sizeof(cbuf) / sizeof(cbuf[0])), cnext);
380
381      if (res == std::codecvt_base::ok || res == std::codecvt_base::partial)
382        {
383          ret += std::string(cbuf, cnext);
384          if (wcend == wcnext)
385            break;
386        }
387      else if (res == std::codecvt_base::noconv)
388        {
389          ret += std::string(wcbegin, wcend);
390          break;
391        }
392      else if (res == std::codecvt_base::error)
393        {
394          throw std::runtime_error
395            ("A character set conversion failed.  Please report this bug.");
396          break;
397        }
398      else
399        break;
400
401      wcbegin = wcnext;
402    }
403
404  return ret;
405}
406
407std::string
408sbuild::find_program_in_path (std::string const& program,
409                              std::string const& path,
410                              std::string const& prefix)
411{
412  if (program.find_first_of('/') != std::string::npos)
413    return program;
414
415  string_list dirs = split_string(path, std::string(1, ':'));
416
417  for (string_list::const_iterator dir = dirs.begin();
418       dir != dirs.end();
419       ++dir)
420    {
421      std::string realname = *dir + '/' + program;
422      std::string absname;
423      if (prefix.length() > 0)
424        {
425          absname = prefix;
426          if (dir->length() > 0 && (*dir)[0] != '/')
427            absname += '/';
428        }
429      absname += realname;
430
431      try
432        {
433          if (stat(absname).is_regular() &&
434              access (absname.c_str(), X_OK) == 0)
435            return realname;
436        }
437      catch (std::runtime_error const& e)
438        {
439        }
440    }
441
442  return "";
443}
444
445char **
446sbuild::string_list_to_strv (string_list const& str)
447{
448  char **ret = new char *[str.size() + 1];
449
450  for (string_list::size_type i = 0;
451       i < str.size();
452       ++i)
453    {
454      ret[i] = new char[str[i].length() + 1];
455      std::strcpy(ret[i], str[i].c_str());
456    }
457  ret[str.size()] = 0;
458
459  return ret;
460}
461
462
463void
464sbuild::strv_delete (char **strv)
465{
466  for (char **pos = strv; pos != 0 && *pos != 0; ++pos)
467    delete *pos;
468  delete[] strv;
469}
470
471int
472sbuild::exec (std::string const& file,
473              string_list const& command,
474              environment const& env)
475{
476  char **argv = string_list_to_strv(command);
477  char **envp = env.get_strv();
478  int status;
479
480  if ((status = execve(file.c_str(), argv, envp)) != 0)
481    {
482      strv_delete(argv);
483      strv_delete(envp);
484    }
485
486  return status;
487}
488
489sbuild::stat::stat (const char *file):
490  file(file),
491  fd(0),
492  errorno(0),
493  status()
494{
495  if (::stat(file, &this->status) < 0)
496    this->errorno = errno;
497}
498
499sbuild::stat::stat (std::string const& file):
500  file(file),
501  fd(0),
502  errorno(0),
503  status()
504{
505  if (::stat(file.c_str(), &this->status) < 0)
506    this->errorno = errno;
507}
508
509sbuild::stat::stat (std::string const& file,
510                    int                fd):
511  file(file),
512  fd(fd),
513  errorno(0),
514  status()
515{
516  if (::fstat(fd, &this->status) < 0)
517    this->errorno = errno;
518}
519
520sbuild::stat::stat (int fd):
521  file(),
522  fd(fd),
523  errorno(0),
524  status()
525{
526  if (::fstat(fd, &this->status) < 0)
527    this->errorno = errno;
528}
529
530sbuild::stat::~stat ()
531{
532}
533
534sbuild::passwd::passwd ():
535  ::passwd(),
536  buffer(),
537  valid(false)
538{
539  clear();
540}
541
542sbuild::passwd::passwd (uid_t uid):
543  ::passwd(),
544  buffer(),
545  valid(false)
546{
547  clear();
548
549  query_uid(uid);
550}
551
552sbuild::passwd::passwd (const char *name):
553  ::passwd(),
554  buffer(),
555  valid(false)
556{
557  clear();
558
559  query_name(name);
560}
561
562sbuild::passwd::passwd (std::string const& name):
563  ::passwd(),
564  buffer(),
565  valid(false)
566{
567  clear();
568
569  query_name(name);
570}
571
572void
573sbuild::passwd::clear ()
574{
575  valid = false;
576
577  buffer.clear();
578
579  ::passwd::pw_name = 0;
580  ::passwd::pw_passwd = 0;
581  ::passwd::pw_uid = 0;
582  ::passwd::pw_gid = 0;
583  ::passwd::pw_gecos = 0;
584  ::passwd::pw_dir = 0;
585  ::passwd::pw_shell = 0;
586}
587
588void
589sbuild::passwd::query_uid (uid_t uid)
590{
591  buffer_type::size_type size = 1 << 7;
592  buffer.reserve(size);
593  int error;
594
595  ::passwd *pwd_result;
596
597  while ((error = getpwuid_r(uid, this,
598                             &buffer[0], buffer.capacity(),
599                             &pwd_result)))
600    {
601      size <<= 1;
602      buffer.reserve(size);
603    }
604
605  if (pwd_result)
606    valid = true;
607  else
608    errno = error;
609}
610
611void
612sbuild::passwd::query_name (const char *name)
613{
614  buffer_type::size_type size = 1 << 8;
615  buffer.reserve(size);
616  int error;
617
618  ::passwd *pwd_result;
619
620  while ((error = getpwnam_r(name, this,
621                             &buffer[0], buffer.capacity(),
622                             &pwd_result)))
623    {
624      size <<= 1;
625      buffer.reserve(size);
626    }
627
628  if (pwd_result)
629    valid = true;
630  else
631    errno = error;
632}
633
634void
635sbuild::passwd::query_name (std::string const& name)
636{
637  query_name(name.c_str());
638}
639
640bool
641sbuild::passwd::operator ! () const
642{
643  return !valid;
644}
645
646sbuild::group::group ():
647  ::group(),
648  buffer(),
649  valid(false)
650{
651  clear();
652}
653
654sbuild::group::group (gid_t gid):
655  ::group(),
656  buffer(),
657  valid(false)
658{
659  clear();
660
661  query_gid(gid);
662}
663
664sbuild::group::group (const char *name):
665  ::group(),
666  buffer(),
667  valid(false)
668{
669  clear();
670
671  query_name(name);
672}
673
674sbuild::group::group (std::string const& name):
675  ::group(),
676  buffer(),
677  valid(false)
678{
679  clear();
680
681  query_name(name);
682}
683
684void
685sbuild::group::clear ()
686{
687  valid = false;
688
689  buffer.clear();
690
691  ::group::gr_name = 0;
692  ::group::gr_passwd = 0;
693  ::group::gr_gid = 0;
694  ::group::gr_mem = 0;
695}
696
697void
698sbuild::group::query_gid (gid_t gid)
699{
700  buffer_type::size_type size = 1 << 7;
701  buffer.reserve(size);
702  int error;
703
704  ::group *grp_result;
705
706  while ((error = getgrgid_r(gid, this,
707                             &buffer[0], buffer.capacity(),
708                             &grp_result)))
709    {
710      size <<= 1;
711      buffer.reserve(size);
712    }
713
714  if (grp_result)
715    valid = true;
716  else
717    errno = error;
718}
719
720void
721sbuild::group::query_name (const char *name)
722{
723  buffer_type::size_type size = 1 << 8;
724  buffer.reserve(size);
725  int error;
726
727  ::group *grp_result;
728
729  while ((error = getgrnam_r(name, this,
730                             &buffer[0], buffer.capacity(),
731                             &grp_result)))
732    {
733      size <<= 1;
734      buffer.reserve(size);
735    }
736
737  if (grp_result)
738    valid = true;
739  else
740    errno = error;
741}
742
743void
744sbuild::group::query_name (std::string const& name)
745{
746  query_name(name.c_str());
747}
748
749bool
750sbuild::group::operator ! () const
751{
752  return !valid;
753}
Note: See TracBrowser for help on using the repository browser.