]> git.saurik.com Git - apt.git/blob - apt-pkg/contrib/fileutl.cc
Merge branch 'debian/sid' into debian/experimental
[apt.git] / apt-pkg / contrib / fileutl.cc
1 // -*- mode: cpp; mode: fold -*-
2 // Description /*{{{*/
3 /* ######################################################################
4
5 File Utilities
6
7 CopyFile - Buffered copy of a single file
8 GetLock - dpkg compatible lock file manipulation (fcntl)
9
10 Most of this source is placed in the Public Domain, do with it what
11 you will
12 It was originally written by Jason Gunthorpe <jgg@debian.org>.
13 FileFd gzip support added by Martin Pitt <martin.pitt@canonical.com>
14
15 The exception is RunScripts() it is under the GPLv2
16
17 ##################################################################### */
18 /*}}}*/
19 // Include Files /*{{{*/
20 #include <config.h>
21
22 #include <apt-pkg/fileutl.h>
23 #include <apt-pkg/strutl.h>
24 #include <apt-pkg/error.h>
25 #include <apt-pkg/sptr.h>
26 #include <apt-pkg/aptconfiguration.h>
27 #include <apt-pkg/configuration.h>
28 #include <apt-pkg/macros.h>
29
30 #include <ctype.h>
31 #include <stdarg.h>
32 #include <stddef.h>
33 #include <sys/select.h>
34 #include <time.h>
35 #include <string>
36 #include <vector>
37 #include <cstdlib>
38 #include <cstring>
39 #include <cstdio>
40 #include <iostream>
41 #include <unistd.h>
42 #include <fcntl.h>
43 #include <sys/stat.h>
44 #include <sys/time.h>
45 #include <sys/wait.h>
46 #include <dirent.h>
47 #include <signal.h>
48 #include <errno.h>
49 #include <glob.h>
50
51 #include <set>
52 #include <algorithm>
53
54 #ifdef HAVE_ZLIB
55 #include <zlib.h>
56 #endif
57 #ifdef HAVE_BZ2
58 #include <bzlib.h>
59 #endif
60 #ifdef HAVE_LZMA
61 #include <lzma.h>
62 #endif
63 #include <endian.h>
64 #include <stdint.h>
65
66 #include <apti18n.h>
67 /*}}}*/
68
69 using namespace std;
70
71 // RunScripts - Run a set of scripts from a configuration subtree /*{{{*/
72 // ---------------------------------------------------------------------
73 /* */
74 bool RunScripts(const char *Cnf)
75 {
76 Configuration::Item const *Opts = _config->Tree(Cnf);
77 if (Opts == 0 || Opts->Child == 0)
78 return true;
79 Opts = Opts->Child;
80
81 // Fork for running the system calls
82 pid_t Child = ExecFork();
83
84 // This is the child
85 if (Child == 0)
86 {
87 if (_config->FindDir("DPkg::Chroot-Directory","/") != "/")
88 {
89 std::cerr << "Chrooting into "
90 << _config->FindDir("DPkg::Chroot-Directory")
91 << std::endl;
92 if (chroot(_config->FindDir("DPkg::Chroot-Directory","/").c_str()) != 0)
93 _exit(100);
94 }
95
96 if (chdir("/tmp/") != 0)
97 _exit(100);
98
99 unsigned int Count = 1;
100 for (; Opts != 0; Opts = Opts->Next, Count++)
101 {
102 if (Opts->Value.empty() == true)
103 continue;
104
105 if(_config->FindB("Debug::RunScripts", false) == true)
106 std::clog << "Running external script: '"
107 << Opts->Value << "'" << std::endl;
108
109 if (system(Opts->Value.c_str()) != 0)
110 _exit(100+Count);
111 }
112 _exit(0);
113 }
114
115 // Wait for the child
116 int Status = 0;
117 while (waitpid(Child,&Status,0) != Child)
118 {
119 if (errno == EINTR)
120 continue;
121 return _error->Errno("waitpid","Couldn't wait for subprocess");
122 }
123
124 // Restore sig int/quit
125 signal(SIGQUIT,SIG_DFL);
126 signal(SIGINT,SIG_DFL);
127
128 // Check for an error code.
129 if (WIFEXITED(Status) == 0 || WEXITSTATUS(Status) != 0)
130 {
131 unsigned int Count = WEXITSTATUS(Status);
132 if (Count > 100)
133 {
134 Count -= 100;
135 for (; Opts != 0 && Count != 1; Opts = Opts->Next, Count--);
136 _error->Error("Problem executing scripts %s '%s'",Cnf,Opts->Value.c_str());
137 }
138
139 return _error->Error("Sub-process returned an error code");
140 }
141
142 return true;
143 }
144 /*}}}*/
145
146 // CopyFile - Buffered copy of a file /*{{{*/
147 // ---------------------------------------------------------------------
148 /* The caller is expected to set things so that failure causes erasure */
149 bool CopyFile(FileFd &From,FileFd &To)
150 {
151 if (From.IsOpen() == false || To.IsOpen() == false ||
152 From.Failed() == true || To.Failed() == true)
153 return false;
154
155 // Buffered copy between fds
156 SPtrArray<unsigned char> Buf = new unsigned char[64000];
157 unsigned long long Size = From.Size();
158 while (Size != 0)
159 {
160 unsigned long long ToRead = Size;
161 if (Size > 64000)
162 ToRead = 64000;
163
164 if (From.Read(Buf,ToRead) == false ||
165 To.Write(Buf,ToRead) == false)
166 return false;
167
168 Size -= ToRead;
169 }
170
171 return true;
172 }
173 /*}}}*/
174 // GetLock - Gets a lock file /*{{{*/
175 // ---------------------------------------------------------------------
176 /* This will create an empty file of the given name and lock it. Once this
177 is done all other calls to GetLock in any other process will fail with
178 -1. The return result is the fd of the file, the call should call
179 close at some time. */
180 int GetLock(string File,bool Errors)
181 {
182 // GetLock() is used in aptitude on directories with public-write access
183 // Use O_NOFOLLOW here to prevent symlink traversal attacks
184 int FD = open(File.c_str(),O_RDWR | O_CREAT | O_NOFOLLOW,0640);
185 if (FD < 0)
186 {
187 // Read only .. can't have locking problems there.
188 if (errno == EROFS)
189 {
190 _error->Warning(_("Not using locking for read only lock file %s"),File.c_str());
191 return dup(0); // Need something for the caller to close
192 }
193
194 if (Errors == true)
195 _error->Errno("open",_("Could not open lock file %s"),File.c_str());
196
197 // Feh.. We do this to distinguish the lock vs open case..
198 errno = EPERM;
199 return -1;
200 }
201 SetCloseExec(FD,true);
202
203 // Acquire a write lock
204 struct flock fl;
205 fl.l_type = F_WRLCK;
206 fl.l_whence = SEEK_SET;
207 fl.l_start = 0;
208 fl.l_len = 0;
209 if (fcntl(FD,F_SETLK,&fl) == -1)
210 {
211 // always close to not leak resources
212 int Tmp = errno;
213 close(FD);
214 errno = Tmp;
215
216 if (errno == ENOLCK)
217 {
218 _error->Warning(_("Not using locking for nfs mounted lock file %s"),File.c_str());
219 return dup(0); // Need something for the caller to close
220 }
221
222 if (Errors == true)
223 _error->Errno("open",_("Could not get lock %s"),File.c_str());
224
225 return -1;
226 }
227
228 return FD;
229 }
230 /*}}}*/
231 // FileExists - Check if a file exists /*{{{*/
232 // ---------------------------------------------------------------------
233 /* Beware: Directories are also files! */
234 bool FileExists(string File)
235 {
236 struct stat Buf;
237 if (stat(File.c_str(),&Buf) != 0)
238 return false;
239 return true;
240 }
241 /*}}}*/
242 // RealFileExists - Check if a file exists and if it is really a file /*{{{*/
243 // ---------------------------------------------------------------------
244 /* */
245 bool RealFileExists(string File)
246 {
247 struct stat Buf;
248 if (stat(File.c_str(),&Buf) != 0)
249 return false;
250 return ((Buf.st_mode & S_IFREG) != 0);
251 }
252 /*}}}*/
253 // DirectoryExists - Check if a directory exists and is really one /*{{{*/
254 // ---------------------------------------------------------------------
255 /* */
256 bool DirectoryExists(string const &Path)
257 {
258 struct stat Buf;
259 if (stat(Path.c_str(),&Buf) != 0)
260 return false;
261 return ((Buf.st_mode & S_IFDIR) != 0);
262 }
263 /*}}}*/
264 // CreateDirectory - poor man's mkdir -p guarded by a parent directory /*{{{*/
265 // ---------------------------------------------------------------------
266 /* This method will create all directories needed for path in good old
267 mkdir -p style but refuses to do this if Parent is not a prefix of
268 this Path. Example: /var/cache/ and /var/cache/apt/archives are given,
269 so it will create apt/archives if /var/cache exists - on the other
270 hand if the parent is /var/lib the creation will fail as this path
271 is not a parent of the path to be generated. */
272 bool CreateDirectory(string const &Parent, string const &Path)
273 {
274 if (Parent.empty() == true || Path.empty() == true)
275 return false;
276
277 if (DirectoryExists(Path) == true)
278 return true;
279
280 if (DirectoryExists(Parent) == false)
281 return false;
282
283 // we are not going to create directories "into the blue"
284 if (Path.compare(0, Parent.length(), Parent) != 0)
285 return false;
286
287 vector<string> const dirs = VectorizeString(Path.substr(Parent.size()), '/');
288 string progress = Parent;
289 for (vector<string>::const_iterator d = dirs.begin(); d != dirs.end(); ++d)
290 {
291 if (d->empty() == true)
292 continue;
293
294 progress.append("/").append(*d);
295 if (DirectoryExists(progress) == true)
296 continue;
297
298 if (mkdir(progress.c_str(), 0755) != 0)
299 return false;
300 }
301 return true;
302 }
303 /*}}}*/
304 // CreateAPTDirectoryIfNeeded - ensure that the given directory exists /*{{{*/
305 // ---------------------------------------------------------------------
306 /* a small wrapper around CreateDirectory to check if it exists and to
307 remove the trailing "/apt/" from the parent directory if needed */
308 bool CreateAPTDirectoryIfNeeded(string const &Parent, string const &Path)
309 {
310 if (DirectoryExists(Path) == true)
311 return true;
312
313 size_t const len = Parent.size();
314 if (len > 5 && Parent.find("/apt/", len - 6, 5) == len - 5)
315 {
316 if (CreateDirectory(Parent.substr(0,len-5), Path) == true)
317 return true;
318 }
319 else if (CreateDirectory(Parent, Path) == true)
320 return true;
321
322 return false;
323 }
324 /*}}}*/
325 // GetListOfFilesInDir - returns a vector of files in the given dir /*{{{*/
326 // ---------------------------------------------------------------------
327 /* If an extension is given only files with this extension are included
328 in the returned vector, otherwise every "normal" file is included. */
329 std::vector<string> GetListOfFilesInDir(string const &Dir, string const &Ext,
330 bool const &SortList, bool const &AllowNoExt)
331 {
332 std::vector<string> ext;
333 ext.reserve(2);
334 if (Ext.empty() == false)
335 ext.push_back(Ext);
336 if (AllowNoExt == true && ext.empty() == false)
337 ext.push_back("");
338 return GetListOfFilesInDir(Dir, ext, SortList);
339 }
340 std::vector<string> GetListOfFilesInDir(string const &Dir, std::vector<string> const &Ext,
341 bool const &SortList)
342 {
343 // Attention debuggers: need to be set with the environment config file!
344 bool const Debug = _config->FindB("Debug::GetListOfFilesInDir", false);
345 if (Debug == true)
346 {
347 std::clog << "Accept in " << Dir << " only files with the following " << Ext.size() << " extensions:" << std::endl;
348 if (Ext.empty() == true)
349 std::clog << "\tNO extension" << std::endl;
350 else
351 for (std::vector<string>::const_iterator e = Ext.begin();
352 e != Ext.end(); ++e)
353 std::clog << '\t' << (e->empty() == true ? "NO" : *e) << " extension" << std::endl;
354 }
355
356 std::vector<string> List;
357
358 if (DirectoryExists(Dir) == false)
359 {
360 _error->Error(_("List of files can't be created as '%s' is not a directory"), Dir.c_str());
361 return List;
362 }
363
364 Configuration::MatchAgainstConfig SilentIgnore("Dir::Ignore-Files-Silently");
365 DIR *D = opendir(Dir.c_str());
366 if (D == 0)
367 {
368 _error->Errno("opendir",_("Unable to read %s"),Dir.c_str());
369 return List;
370 }
371
372 for (struct dirent *Ent = readdir(D); Ent != 0; Ent = readdir(D))
373 {
374 // skip "hidden" files
375 if (Ent->d_name[0] == '.')
376 continue;
377
378 // Make sure it is a file and not something else
379 string const File = flCombine(Dir,Ent->d_name);
380 #ifdef _DIRENT_HAVE_D_TYPE
381 if (Ent->d_type != DT_REG)
382 #endif
383 {
384 if (RealFileExists(File) == false)
385 {
386 // do not show ignoration warnings for directories
387 if (
388 #ifdef _DIRENT_HAVE_D_TYPE
389 Ent->d_type == DT_DIR ||
390 #endif
391 DirectoryExists(File) == true)
392 continue;
393 if (SilentIgnore.Match(Ent->d_name) == false)
394 _error->Notice(_("Ignoring '%s' in directory '%s' as it is not a regular file"), Ent->d_name, Dir.c_str());
395 continue;
396 }
397 }
398
399 // check for accepted extension:
400 // no extension given -> periods are bad as hell!
401 // extensions given -> "" extension allows no extension
402 if (Ext.empty() == false)
403 {
404 string d_ext = flExtension(Ent->d_name);
405 if (d_ext == Ent->d_name) // no extension
406 {
407 if (std::find(Ext.begin(), Ext.end(), "") == Ext.end())
408 {
409 if (Debug == true)
410 std::clog << "Bad file: " << Ent->d_name << " → no extension" << std::endl;
411 if (SilentIgnore.Match(Ent->d_name) == false)
412 _error->Notice(_("Ignoring file '%s' in directory '%s' as it has no filename extension"), Ent->d_name, Dir.c_str());
413 continue;
414 }
415 }
416 else if (std::find(Ext.begin(), Ext.end(), d_ext) == Ext.end())
417 {
418 if (Debug == true)
419 std::clog << "Bad file: " << Ent->d_name << " → bad extension »" << flExtension(Ent->d_name) << "«" << std::endl;
420 if (SilentIgnore.Match(Ent->d_name) == false)
421 _error->Notice(_("Ignoring file '%s' in directory '%s' as it has an invalid filename extension"), Ent->d_name, Dir.c_str());
422 continue;
423 }
424 }
425
426 // Skip bad filenames ala run-parts
427 const char *C = Ent->d_name;
428 for (; *C != 0; ++C)
429 if (isalpha(*C) == 0 && isdigit(*C) == 0
430 && *C != '_' && *C != '-' && *C != ':') {
431 // no required extension -> dot is a bad character
432 if (*C == '.' && Ext.empty() == false)
433 continue;
434 break;
435 }
436
437 // we don't reach the end of the name -> bad character included
438 if (*C != 0)
439 {
440 if (Debug == true)
441 std::clog << "Bad file: " << Ent->d_name << " → bad character »"
442 << *C << "« in filename (period allowed: " << (Ext.empty() ? "no" : "yes") << ")" << std::endl;
443 continue;
444 }
445
446 // skip filenames which end with a period. These are never valid
447 if (*(C - 1) == '.')
448 {
449 if (Debug == true)
450 std::clog << "Bad file: " << Ent->d_name << " → Period as last character" << std::endl;
451 continue;
452 }
453
454 if (Debug == true)
455 std::clog << "Accept file: " << Ent->d_name << " in " << Dir << std::endl;
456 List.push_back(File);
457 }
458 closedir(D);
459
460 if (SortList == true)
461 std::sort(List.begin(),List.end());
462 return List;
463 }
464 std::vector<string> GetListOfFilesInDir(string const &Dir, bool SortList)
465 {
466 bool const Debug = _config->FindB("Debug::GetListOfFilesInDir", false);
467 if (Debug == true)
468 std::clog << "Accept in " << Dir << " all regular files" << std::endl;
469
470 std::vector<string> List;
471
472 if (DirectoryExists(Dir) == false)
473 {
474 _error->Error(_("List of files can't be created as '%s' is not a directory"), Dir.c_str());
475 return List;
476 }
477
478 DIR *D = opendir(Dir.c_str());
479 if (D == 0)
480 {
481 _error->Errno("opendir",_("Unable to read %s"),Dir.c_str());
482 return List;
483 }
484
485 for (struct dirent *Ent = readdir(D); Ent != 0; Ent = readdir(D))
486 {
487 // skip "hidden" files
488 if (Ent->d_name[0] == '.')
489 continue;
490
491 // Make sure it is a file and not something else
492 string const File = flCombine(Dir,Ent->d_name);
493 #ifdef _DIRENT_HAVE_D_TYPE
494 if (Ent->d_type != DT_REG)
495 #endif
496 {
497 if (RealFileExists(File) == false)
498 {
499 if (Debug == true)
500 std::clog << "Bad file: " << Ent->d_name << " → it is not a real file" << std::endl;
501 continue;
502 }
503 }
504
505 // Skip bad filenames ala run-parts
506 const char *C = Ent->d_name;
507 for (; *C != 0; ++C)
508 if (isalpha(*C) == 0 && isdigit(*C) == 0
509 && *C != '_' && *C != '-' && *C != '.')
510 break;
511
512 // we don't reach the end of the name -> bad character included
513 if (*C != 0)
514 {
515 if (Debug == true)
516 std::clog << "Bad file: " << Ent->d_name << " → bad character »" << *C << "« in filename" << std::endl;
517 continue;
518 }
519
520 // skip filenames which end with a period. These are never valid
521 if (*(C - 1) == '.')
522 {
523 if (Debug == true)
524 std::clog << "Bad file: " << Ent->d_name << " → Period as last character" << std::endl;
525 continue;
526 }
527
528 if (Debug == true)
529 std::clog << "Accept file: " << Ent->d_name << " in " << Dir << std::endl;
530 List.push_back(File);
531 }
532 closedir(D);
533
534 if (SortList == true)
535 std::sort(List.begin(),List.end());
536 return List;
537 }
538 /*}}}*/
539 // SafeGetCWD - This is a safer getcwd that returns a dynamic string /*{{{*/
540 // ---------------------------------------------------------------------
541 /* We return / on failure. */
542 string SafeGetCWD()
543 {
544 // Stash the current dir.
545 char S[300];
546 S[0] = 0;
547 if (getcwd(S,sizeof(S)-2) == 0)
548 return "/";
549 unsigned int Len = strlen(S);
550 S[Len] = '/';
551 S[Len+1] = 0;
552 return S;
553 }
554 /*}}}*/
555 // GetModificationTime - Get the mtime of the given file or -1 on error /*{{{*/
556 // ---------------------------------------------------------------------
557 /* We return / on failure. */
558 time_t GetModificationTime(string const &Path)
559 {
560 struct stat St;
561 if (stat(Path.c_str(), &St) < 0)
562 return -1;
563 return St.st_mtime;
564 }
565 /*}}}*/
566 // flNotDir - Strip the directory from the filename /*{{{*/
567 // ---------------------------------------------------------------------
568 /* */
569 string flNotDir(string File)
570 {
571 string::size_type Res = File.rfind('/');
572 if (Res == string::npos)
573 return File;
574 Res++;
575 return string(File,Res,Res - File.length());
576 }
577 /*}}}*/
578 // flNotFile - Strip the file from the directory name /*{{{*/
579 // ---------------------------------------------------------------------
580 /* Result ends in a / */
581 string flNotFile(string File)
582 {
583 string::size_type Res = File.rfind('/');
584 if (Res == string::npos)
585 return "./";
586 Res++;
587 return string(File,0,Res);
588 }
589 /*}}}*/
590 // flExtension - Return the extension for the file /*{{{*/
591 // ---------------------------------------------------------------------
592 /* */
593 string flExtension(string File)
594 {
595 string::size_type Res = File.rfind('.');
596 if (Res == string::npos)
597 return File;
598 Res++;
599 return string(File,Res,Res - File.length());
600 }
601 /*}}}*/
602 // flNoLink - If file is a symlink then deref it /*{{{*/
603 // ---------------------------------------------------------------------
604 /* If the name is not a link then the returned path is the input. */
605 string flNoLink(string File)
606 {
607 struct stat St;
608 if (lstat(File.c_str(),&St) != 0 || S_ISLNK(St.st_mode) == 0)
609 return File;
610 if (stat(File.c_str(),&St) != 0)
611 return File;
612
613 /* Loop resolving the link. There is no need to limit the number of
614 loops because the stat call above ensures that the symlink is not
615 circular */
616 char Buffer[1024];
617 string NFile = File;
618 while (1)
619 {
620 // Read the link
621 ssize_t Res;
622 if ((Res = readlink(NFile.c_str(),Buffer,sizeof(Buffer))) <= 0 ||
623 (size_t)Res >= sizeof(Buffer))
624 return File;
625
626 // Append or replace the previous path
627 Buffer[Res] = 0;
628 if (Buffer[0] == '/')
629 NFile = Buffer;
630 else
631 NFile = flNotFile(NFile) + Buffer;
632
633 // See if we are done
634 if (lstat(NFile.c_str(),&St) != 0)
635 return File;
636 if (S_ISLNK(St.st_mode) == 0)
637 return NFile;
638 }
639 }
640 /*}}}*/
641 // flCombine - Combine a file and a directory /*{{{*/
642 // ---------------------------------------------------------------------
643 /* If the file is an absolute path then it is just returned, otherwise
644 the directory is pre-pended to it. */
645 string flCombine(string Dir,string File)
646 {
647 if (File.empty() == true)
648 return string();
649
650 if (File[0] == '/' || Dir.empty() == true)
651 return File;
652 if (File.length() >= 2 && File[0] == '.' && File[1] == '/')
653 return File;
654 if (Dir[Dir.length()-1] == '/')
655 return Dir + File;
656 return Dir + '/' + File;
657 }
658 /*}}}*/
659 // flAbsPath - Return the absolute path of the filename /*{{{*/
660 // ---------------------------------------------------------------------
661 /* */
662 string flAbsPath(string File)
663 {
664 char *p = realpath(File.c_str(), NULL);
665 if (p == NULL)
666 {
667 _error->Errno("realpath", "flAbsPath failed");
668 return "";
669 }
670 std::string AbsPath(p);
671 free(p);
672 return AbsPath;
673 }
674 /*}}}*/
675 // SetCloseExec - Set the close on exec flag /*{{{*/
676 // ---------------------------------------------------------------------
677 /* */
678 void SetCloseExec(int Fd,bool Close)
679 {
680 if (fcntl(Fd,F_SETFD,(Close == false)?0:FD_CLOEXEC) != 0)
681 {
682 cerr << "FATAL -> Could not set close on exec " << strerror(errno) << endl;
683 exit(100);
684 }
685 }
686 /*}}}*/
687 // SetNonBlock - Set the nonblocking flag /*{{{*/
688 // ---------------------------------------------------------------------
689 /* */
690 void SetNonBlock(int Fd,bool Block)
691 {
692 int Flags = fcntl(Fd,F_GETFL) & (~O_NONBLOCK);
693 if (fcntl(Fd,F_SETFL,Flags | ((Block == false)?0:O_NONBLOCK)) != 0)
694 {
695 cerr << "FATAL -> Could not set non-blocking flag " << strerror(errno) << endl;
696 exit(100);
697 }
698 }
699 /*}}}*/
700 // WaitFd - Wait for a FD to become readable /*{{{*/
701 // ---------------------------------------------------------------------
702 /* This waits for a FD to become readable using select. It is useful for
703 applications making use of non-blocking sockets. The timeout is
704 in seconds. */
705 bool WaitFd(int Fd,bool write,unsigned long timeout)
706 {
707 fd_set Set;
708 struct timeval tv;
709 FD_ZERO(&Set);
710 FD_SET(Fd,&Set);
711 tv.tv_sec = timeout;
712 tv.tv_usec = 0;
713 if (write == true)
714 {
715 int Res;
716 do
717 {
718 Res = select(Fd+1,0,&Set,0,(timeout != 0?&tv:0));
719 }
720 while (Res < 0 && errno == EINTR);
721
722 if (Res <= 0)
723 return false;
724 }
725 else
726 {
727 int Res;
728 do
729 {
730 Res = select(Fd+1,&Set,0,0,(timeout != 0?&tv:0));
731 }
732 while (Res < 0 && errno == EINTR);
733
734 if (Res <= 0)
735 return false;
736 }
737
738 return true;
739 }
740 /*}}}*/
741 // MergeKeepFdsFromConfiguration - Merge APT::Keep-Fds configuration /*{{{*/
742 // ---------------------------------------------------------------------
743 /* This is used to merge the APT::Keep-Fds with the provided KeepFDs
744 * set.
745 */
746 void MergeKeepFdsFromConfiguration(std::set<int> &KeepFDs)
747 {
748 Configuration::Item const *Opts = _config->Tree("APT::Keep-Fds");
749 if (Opts != 0 && Opts->Child != 0)
750 {
751 Opts = Opts->Child;
752 for (; Opts != 0; Opts = Opts->Next)
753 {
754 if (Opts->Value.empty() == true)
755 continue;
756 int fd = atoi(Opts->Value.c_str());
757 KeepFDs.insert(fd);
758 }
759 }
760 }
761 /*}}}*/
762 // ExecFork - Magical fork that sanitizes the context before execing /*{{{*/
763 // ---------------------------------------------------------------------
764 /* This is used if you want to cleanse the environment for the forked
765 child, it fixes up the important signals and nukes all of the fds,
766 otherwise acts like normal fork. */
767 pid_t ExecFork()
768 {
769 set<int> KeepFDs;
770 // we need to merge the Keep-Fds as external tools like
771 // debconf-apt-progress use it
772 MergeKeepFdsFromConfiguration(KeepFDs);
773 return ExecFork(KeepFDs);
774 }
775
776 pid_t ExecFork(std::set<int> KeepFDs)
777 {
778 // Fork off the process
779 pid_t Process = fork();
780 if (Process < 0)
781 {
782 cerr << "FATAL -> Failed to fork." << endl;
783 exit(100);
784 }
785
786 // Spawn the subprocess
787 if (Process == 0)
788 {
789 // Setup the signals
790 signal(SIGPIPE,SIG_DFL);
791 signal(SIGQUIT,SIG_DFL);
792 signal(SIGINT,SIG_DFL);
793 signal(SIGWINCH,SIG_DFL);
794 signal(SIGCONT,SIG_DFL);
795 signal(SIGTSTP,SIG_DFL);
796
797 // Close all of our FDs - just in case
798 for (int K = 3; K != sysconf(_SC_OPEN_MAX); K++)
799 {
800 if(KeepFDs.find(K) == KeepFDs.end())
801 fcntl(K,F_SETFD,FD_CLOEXEC);
802 }
803 }
804
805 return Process;
806 }
807 /*}}}*/
808 // ExecWait - Fancy waitpid /*{{{*/
809 // ---------------------------------------------------------------------
810 /* Waits for the given sub process. If Reap is set then no errors are
811 generated. Otherwise a failed subprocess will generate a proper descriptive
812 message */
813 bool ExecWait(pid_t Pid,const char *Name,bool Reap)
814 {
815 if (Pid <= 1)
816 return true;
817
818 // Wait and collect the error code
819 int Status;
820 while (waitpid(Pid,&Status,0) != Pid)
821 {
822 if (errno == EINTR)
823 continue;
824
825 if (Reap == true)
826 return false;
827
828 return _error->Error(_("Waited for %s but it wasn't there"),Name);
829 }
830
831
832 // Check for an error code.
833 if (WIFEXITED(Status) == 0 || WEXITSTATUS(Status) != 0)
834 {
835 if (Reap == true)
836 return false;
837 if (WIFSIGNALED(Status) != 0)
838 {
839 if( WTERMSIG(Status) == SIGSEGV)
840 return _error->Error(_("Sub-process %s received a segmentation fault."),Name);
841 else
842 return _error->Error(_("Sub-process %s received signal %u."),Name, WTERMSIG(Status));
843 }
844
845 if (WIFEXITED(Status) != 0)
846 return _error->Error(_("Sub-process %s returned an error code (%u)"),Name,WEXITSTATUS(Status));
847
848 return _error->Error(_("Sub-process %s exited unexpectedly"),Name);
849 }
850
851 return true;
852 }
853 /*}}}*/
854
855
856 // StartsWithGPGClearTextSignature - Check if a file is Pgp/GPG clearsigned /*{{{*/
857 // ---------------------------------------------------------------------
858 /* */
859 bool StartsWithGPGClearTextSignature(string const &FileName)
860 {
861 static const char* SIGMSG = "-----BEGIN PGP SIGNED MESSAGE-----\n";
862 char buffer[strlen(SIGMSG)+1];
863 FILE* gpg = fopen(FileName.c_str(), "r");
864 if (gpg == NULL)
865 return false;
866
867 char const * const test = fgets(buffer, sizeof(buffer), gpg);
868 fclose(gpg);
869 if (test == NULL || strcmp(buffer, SIGMSG) != 0)
870 return false;
871
872 return true;
873 }
874
875
876 class FileFdPrivate { /*{{{*/
877 public:
878 #ifdef HAVE_ZLIB
879 gzFile gz;
880 #endif
881 #ifdef HAVE_BZ2
882 BZFILE* bz2;
883 #endif
884 #ifdef HAVE_LZMA
885 struct LZMAFILE {
886 FILE* file;
887 uint8_t buffer[4096];
888 lzma_stream stream;
889 lzma_ret err;
890 bool eof;
891 bool compressing;
892
893 LZMAFILE() : file(NULL), eof(false), compressing(false) {}
894 ~LZMAFILE() {
895 if (compressing == true)
896 {
897 for (;;) {
898 stream.avail_out = sizeof(buffer)/sizeof(buffer[0]);
899 stream.next_out = buffer;
900 err = lzma_code(&stream, LZMA_FINISH);
901 if (err != LZMA_OK && err != LZMA_STREAM_END)
902 {
903 _error->Error("~LZMAFILE: Compress finalisation failed");
904 break;
905 }
906 size_t const n = sizeof(buffer)/sizeof(buffer[0]) - stream.avail_out;
907 if (n && fwrite(buffer, 1, n, file) != n)
908 {
909 _error->Errno("~LZMAFILE",_("Write error"));
910 break;
911 }
912 if (err == LZMA_STREAM_END)
913 break;
914 }
915 }
916 lzma_end(&stream);
917 fclose(file);
918 }
919 };
920 LZMAFILE* lzma;
921 #endif
922 int compressed_fd;
923 pid_t compressor_pid;
924 bool pipe;
925 APT::Configuration::Compressor compressor;
926 unsigned int openmode;
927 unsigned long long seekpos;
928 FileFdPrivate() :
929 #ifdef HAVE_ZLIB
930 gz(NULL),
931 #endif
932 #ifdef HAVE_BZ2
933 bz2(NULL),
934 #endif
935 #ifdef HAVE_LZMA
936 lzma(NULL),
937 #endif
938 compressed_fd(-1), compressor_pid(-1), pipe(false),
939 openmode(0), seekpos(0) {};
940 bool InternalClose(std::string const &FileName)
941 {
942 if (false)
943 /* dummy so that the rest can be 'else if's */;
944 #ifdef HAVE_ZLIB
945 else if (gz != NULL) {
946 int const e = gzclose(gz);
947 gz = NULL;
948 // gzdclose() on empty files always fails with "buffer error" here, ignore that
949 if (e != 0 && e != Z_BUF_ERROR)
950 return _error->Errno("close",_("Problem closing the gzip file %s"), FileName.c_str());
951 }
952 #endif
953 #ifdef HAVE_BZ2
954 else if (bz2 != NULL) {
955 BZ2_bzclose(bz2);
956 bz2 = NULL;
957 }
958 #endif
959 #ifdef HAVE_LZMA
960 else if (lzma != NULL) {
961 delete lzma;
962 lzma = NULL;
963 }
964 #endif
965 return true;
966 }
967 bool CloseDown(std::string const &FileName)
968 {
969 bool const Res = InternalClose(FileName);
970
971 if (compressor_pid > 0)
972 ExecWait(compressor_pid, "FileFdCompressor", true);
973 compressor_pid = -1;
974
975 return Res;
976 }
977 bool InternalStream() const {
978 return false
979 #ifdef HAVE_BZ2
980 || bz2 != NULL
981 #endif
982 #ifdef HAVE_LZMA
983 || lzma != NULL
984 #endif
985 ;
986 }
987
988
989 ~FileFdPrivate() { CloseDown(""); }
990 };
991 /*}}}*/
992 // FileFd::Open - Open a file /*{{{*/
993 // ---------------------------------------------------------------------
994 /* The most commonly used open mode combinations are given with Mode */
995 bool FileFd::Open(string FileName,unsigned int const Mode,CompressMode Compress, unsigned long const AccessMode)
996 {
997 if (Mode == ReadOnlyGzip)
998 return Open(FileName, ReadOnly, Gzip, AccessMode);
999
1000 if (Compress == Auto && (Mode & WriteOnly) == WriteOnly)
1001 return FileFdError("Autodetection on %s only works in ReadOnly openmode!", FileName.c_str());
1002
1003 std::vector<APT::Configuration::Compressor> const compressors = APT::Configuration::getCompressors();
1004 std::vector<APT::Configuration::Compressor>::const_iterator compressor = compressors.begin();
1005 if (Compress == Auto)
1006 {
1007 for (; compressor != compressors.end(); ++compressor)
1008 {
1009 std::string file = FileName + compressor->Extension;
1010 if (FileExists(file) == false)
1011 continue;
1012 FileName = file;
1013 break;
1014 }
1015 }
1016 else if (Compress == Extension)
1017 {
1018 std::string::size_type const found = FileName.find_last_of('.');
1019 std::string ext;
1020 if (found != std::string::npos)
1021 {
1022 ext = FileName.substr(found);
1023 if (ext == ".new" || ext == ".bak")
1024 {
1025 std::string::size_type const found2 = FileName.find_last_of('.', found - 1);
1026 if (found2 != std::string::npos)
1027 ext = FileName.substr(found2, found - found2);
1028 else
1029 ext.clear();
1030 }
1031 }
1032 for (; compressor != compressors.end(); ++compressor)
1033 if (ext == compressor->Extension)
1034 break;
1035 // no matching extension - assume uncompressed (imagine files like 'example.org_Packages')
1036 if (compressor == compressors.end())
1037 for (compressor = compressors.begin(); compressor != compressors.end(); ++compressor)
1038 if (compressor->Name == ".")
1039 break;
1040 }
1041 else
1042 {
1043 std::string name;
1044 switch (Compress)
1045 {
1046 case None: name = "."; break;
1047 case Gzip: name = "gzip"; break;
1048 case Bzip2: name = "bzip2"; break;
1049 case Lzma: name = "lzma"; break;
1050 case Xz: name = "xz"; break;
1051 case Auto:
1052 case Extension:
1053 // Unreachable
1054 return FileFdError("Opening File %s in None, Auto or Extension should be already handled?!?", FileName.c_str());
1055 }
1056 for (; compressor != compressors.end(); ++compressor)
1057 if (compressor->Name == name)
1058 break;
1059 if (compressor == compressors.end())
1060 return FileFdError("Can't find a configured compressor %s for file %s", name.c_str(), FileName.c_str());
1061 }
1062
1063 if (compressor == compressors.end())
1064 return FileFdError("Can't find a match for specified compressor mode for file %s", FileName.c_str());
1065 return Open(FileName, Mode, *compressor, AccessMode);
1066 }
1067 bool FileFd::Open(string FileName,unsigned int const Mode,APT::Configuration::Compressor const &compressor, unsigned long const AccessMode)
1068 {
1069 Close();
1070 Flags = AutoClose;
1071
1072 if ((Mode & WriteOnly) != WriteOnly && (Mode & (Atomic | Create | Empty | Exclusive)) != 0)
1073 return FileFdError("ReadOnly mode for %s doesn't accept additional flags!", FileName.c_str());
1074 if ((Mode & ReadWrite) == 0)
1075 return FileFdError("No openmode provided in FileFd::Open for %s", FileName.c_str());
1076
1077 if ((Mode & Atomic) == Atomic)
1078 {
1079 Flags |= Replace;
1080 }
1081 else if ((Mode & (Exclusive | Create)) == (Exclusive | Create))
1082 {
1083 // for atomic, this will be done by rename in Close()
1084 unlink(FileName.c_str());
1085 }
1086 if ((Mode & Empty) == Empty)
1087 {
1088 struct stat Buf;
1089 if (lstat(FileName.c_str(),&Buf) == 0 && S_ISLNK(Buf.st_mode))
1090 unlink(FileName.c_str());
1091 }
1092
1093 int fileflags = 0;
1094 #define if_FLAGGED_SET(FLAG, MODE) if ((Mode & FLAG) == FLAG) fileflags |= MODE
1095 if_FLAGGED_SET(ReadWrite, O_RDWR);
1096 else if_FLAGGED_SET(ReadOnly, O_RDONLY);
1097 else if_FLAGGED_SET(WriteOnly, O_WRONLY);
1098
1099 if_FLAGGED_SET(Create, O_CREAT);
1100 if_FLAGGED_SET(Empty, O_TRUNC);
1101 if_FLAGGED_SET(Exclusive, O_EXCL);
1102 #undef if_FLAGGED_SET
1103
1104 if ((Mode & Atomic) == Atomic)
1105 {
1106 char *name = strdup((FileName + ".XXXXXX").c_str());
1107
1108 if((iFd = mkstemp(name)) == -1)
1109 {
1110 free(name);
1111 return FileFdErrno("mkstemp", "Could not create temporary file for %s", FileName.c_str());
1112 }
1113
1114 TemporaryFileName = string(name);
1115 free(name);
1116
1117 // umask() will always set the umask and return the previous value, so
1118 // we first set the umask and then reset it to the old value
1119 mode_t const CurrentUmask = umask(0);
1120 umask(CurrentUmask);
1121 // calculate the actual file permissions (just like open/creat)
1122 mode_t const FilePermissions = (AccessMode & ~CurrentUmask);
1123
1124 if(fchmod(iFd, FilePermissions) == -1)
1125 return FileFdErrno("fchmod", "Could not change permissions for temporary file %s", TemporaryFileName.c_str());
1126 }
1127 else
1128 iFd = open(FileName.c_str(), fileflags, AccessMode);
1129
1130 this->FileName = FileName;
1131 if (iFd == -1 || OpenInternDescriptor(Mode, compressor) == false)
1132 {
1133 if (iFd != -1)
1134 {
1135 close (iFd);
1136 iFd = -1;
1137 }
1138 return FileFdErrno("open",_("Could not open file %s"), FileName.c_str());
1139 }
1140
1141 SetCloseExec(iFd,true);
1142 return true;
1143 }
1144 /*}}}*/
1145 // FileFd::OpenDescriptor - Open a filedescriptor /*{{{*/
1146 // ---------------------------------------------------------------------
1147 /* */
1148 bool FileFd::OpenDescriptor(int Fd, unsigned int const Mode, CompressMode Compress, bool AutoClose)
1149 {
1150 std::vector<APT::Configuration::Compressor> const compressors = APT::Configuration::getCompressors();
1151 std::vector<APT::Configuration::Compressor>::const_iterator compressor = compressors.begin();
1152 std::string name;
1153
1154 // compat with the old API
1155 if (Mode == ReadOnlyGzip && Compress == None)
1156 Compress = Gzip;
1157
1158 switch (Compress)
1159 {
1160 case None: name = "."; break;
1161 case Gzip: name = "gzip"; break;
1162 case Bzip2: name = "bzip2"; break;
1163 case Lzma: name = "lzma"; break;
1164 case Xz: name = "xz"; break;
1165 case Auto:
1166 case Extension:
1167 if (AutoClose == true && Fd != -1)
1168 close(Fd);
1169 return FileFdError("Opening Fd %d in Auto or Extension compression mode is not supported", Fd);
1170 }
1171 for (; compressor != compressors.end(); ++compressor)
1172 if (compressor->Name == name)
1173 break;
1174 if (compressor == compressors.end())
1175 {
1176 if (AutoClose == true && Fd != -1)
1177 close(Fd);
1178 return FileFdError("Can't find a configured compressor %s for file %s", name.c_str(), FileName.c_str());
1179 }
1180 return OpenDescriptor(Fd, Mode, *compressor, AutoClose);
1181 }
1182 bool FileFd::OpenDescriptor(int Fd, unsigned int const Mode, APT::Configuration::Compressor const &compressor, bool AutoClose)
1183 {
1184 Close();
1185 Flags = (AutoClose) ? FileFd::AutoClose : 0;
1186 iFd = Fd;
1187 this->FileName = "";
1188 if (OpenInternDescriptor(Mode, compressor) == false)
1189 {
1190 if (iFd != -1 && (
1191 (Flags & Compressed) == Compressed ||
1192 AutoClose == true))
1193 {
1194 close (iFd);
1195 iFd = -1;
1196 }
1197 return FileFdError(_("Could not open file descriptor %d"), Fd);
1198 }
1199 return true;
1200 }
1201 bool FileFd::OpenInternDescriptor(unsigned int const Mode, APT::Configuration::Compressor const &compressor)
1202 {
1203 if (iFd == -1)
1204 return false;
1205 if (compressor.Name == "." || compressor.Binary.empty() == true)
1206 return true;
1207
1208 #if defined HAVE_ZLIB || defined HAVE_BZ2 || defined HAVE_LZMA
1209 // the API to open files is similar, so setup to avoid code duplicates later
1210 // and while at it ensure that we close before opening (if its a reopen)
1211 void* (*compress_open)(int, const char *) = NULL;
1212 if (false)
1213 /* dummy so that the rest can be 'else if's */;
1214 #define APT_COMPRESS_INIT(NAME,OPEN) \
1215 else if (compressor.Name == NAME) \
1216 { \
1217 compress_open = (void*(*)(int, const char *)) OPEN; \
1218 if (d != NULL) d->InternalClose(FileName); \
1219 }
1220 #ifdef HAVE_ZLIB
1221 APT_COMPRESS_INIT("gzip", gzdopen)
1222 #endif
1223 #ifdef HAVE_BZ2
1224 APT_COMPRESS_INIT("bzip2", BZ2_bzdopen)
1225 #endif
1226 #ifdef HAVE_LZMA
1227 APT_COMPRESS_INIT("xz", fdopen)
1228 APT_COMPRESS_INIT("lzma", fdopen)
1229 #endif
1230 #undef APT_COMPRESS_INIT
1231 #endif
1232
1233 if (d == NULL)
1234 {
1235 d = new FileFdPrivate();
1236 d->openmode = Mode;
1237 d->compressor = compressor;
1238 #if defined HAVE_ZLIB || defined HAVE_BZ2 || defined HAVE_LZMA
1239 if ((Flags & AutoClose) != AutoClose && compress_open != NULL)
1240 {
1241 // Need to duplicate fd here or gz/bz2 close for cleanup will close the fd as well
1242 int const internFd = dup(iFd);
1243 if (internFd == -1)
1244 return FileFdErrno("OpenInternDescriptor", _("Could not open file descriptor %d"), iFd);
1245 iFd = internFd;
1246 }
1247 #endif
1248 }
1249
1250 #if defined HAVE_ZLIB || defined HAVE_BZ2 || defined HAVE_LZMA
1251 if (compress_open != NULL)
1252 {
1253 void* compress_struct = NULL;
1254 if ((Mode & ReadWrite) == ReadWrite)
1255 compress_struct = compress_open(iFd, "r+");
1256 else if ((Mode & WriteOnly) == WriteOnly)
1257 compress_struct = compress_open(iFd, "w");
1258 else
1259 compress_struct = compress_open(iFd, "r");
1260 if (compress_struct == NULL)
1261 return false;
1262
1263 if (false)
1264 /* dummy so that the rest can be 'else if's */;
1265 #ifdef HAVE_ZLIB
1266 else if (compressor.Name == "gzip")
1267 d->gz = (gzFile) compress_struct;
1268 #endif
1269 #ifdef HAVE_BZ2
1270 else if (compressor.Name == "bzip2")
1271 d->bz2 = (BZFILE*) compress_struct;
1272 #endif
1273 #ifdef HAVE_LZMA
1274 else if (compressor.Name == "xz" || compressor.Name == "lzma")
1275 {
1276 uint32_t const xzlevel = 6;
1277 uint64_t const memlimit = UINT64_MAX;
1278 if (d->lzma == NULL)
1279 d->lzma = new FileFdPrivate::LZMAFILE;
1280 d->lzma->file = (FILE*) compress_struct;
1281 lzma_stream tmp_stream = LZMA_STREAM_INIT;
1282 d->lzma->stream = tmp_stream;
1283
1284 if ((Mode & ReadWrite) == ReadWrite)
1285 return FileFdError("ReadWrite mode is not supported for file %s", FileName.c_str());
1286
1287 if ((Mode & WriteOnly) == WriteOnly)
1288 {
1289 if (compressor.Name == "xz")
1290 {
1291 if (lzma_easy_encoder(&d->lzma->stream, xzlevel, LZMA_CHECK_CRC32) != LZMA_OK)
1292 return false;
1293 }
1294 else
1295 {
1296 lzma_options_lzma options;
1297 lzma_lzma_preset(&options, xzlevel);
1298 if (lzma_alone_encoder(&d->lzma->stream, &options) != LZMA_OK)
1299 return false;
1300 }
1301 d->lzma->compressing = true;
1302 }
1303 else
1304 {
1305 if (compressor.Name == "xz")
1306 {
1307 if (lzma_auto_decoder(&d->lzma->stream, memlimit, 0) != LZMA_OK)
1308 return false;
1309 }
1310 else
1311 {
1312 if (lzma_alone_decoder(&d->lzma->stream, memlimit) != LZMA_OK)
1313 return false;
1314 }
1315 d->lzma->compressing = false;
1316 }
1317 }
1318 #endif
1319 Flags |= Compressed;
1320 return true;
1321 }
1322 #endif
1323
1324 // collect zombies here in case we reopen
1325 if (d->compressor_pid > 0)
1326 ExecWait(d->compressor_pid, "FileFdCompressor", true);
1327
1328 if ((Mode & ReadWrite) == ReadWrite)
1329 return FileFdError("ReadWrite mode is not supported for file %s", FileName.c_str());
1330
1331 bool const Comp = (Mode & WriteOnly) == WriteOnly;
1332 if (Comp == false)
1333 {
1334 // Handle 'decompression' of empty files
1335 struct stat Buf;
1336 fstat(iFd, &Buf);
1337 if (Buf.st_size == 0 && S_ISFIFO(Buf.st_mode) == false)
1338 return true;
1339
1340 // We don't need the file open - instead let the compressor open it
1341 // as he properly knows better how to efficiently read from 'his' file
1342 if (FileName.empty() == false)
1343 {
1344 close(iFd);
1345 iFd = -1;
1346 }
1347 }
1348
1349 // Create a data pipe
1350 int Pipe[2] = {-1,-1};
1351 if (pipe(Pipe) != 0)
1352 return FileFdErrno("pipe",_("Failed to create subprocess IPC"));
1353 for (int J = 0; J != 2; J++)
1354 SetCloseExec(Pipe[J],true);
1355
1356 d->compressed_fd = iFd;
1357 d->pipe = true;
1358
1359 if (Comp == true)
1360 iFd = Pipe[1];
1361 else
1362 iFd = Pipe[0];
1363
1364 // The child..
1365 d->compressor_pid = ExecFork();
1366 if (d->compressor_pid == 0)
1367 {
1368 if (Comp == true)
1369 {
1370 dup2(d->compressed_fd,STDOUT_FILENO);
1371 dup2(Pipe[0],STDIN_FILENO);
1372 }
1373 else
1374 {
1375 if (d->compressed_fd != -1)
1376 dup2(d->compressed_fd,STDIN_FILENO);
1377 dup2(Pipe[1],STDOUT_FILENO);
1378 }
1379 int const nullfd = open("/dev/null", O_WRONLY);
1380 if (nullfd != -1)
1381 {
1382 dup2(nullfd,STDERR_FILENO);
1383 close(nullfd);
1384 }
1385
1386 SetCloseExec(STDOUT_FILENO,false);
1387 SetCloseExec(STDIN_FILENO,false);
1388
1389 std::vector<char const*> Args;
1390 Args.push_back(compressor.Binary.c_str());
1391 std::vector<std::string> const * const addArgs =
1392 (Comp == true) ? &(compressor.CompressArgs) : &(compressor.UncompressArgs);
1393 for (std::vector<std::string>::const_iterator a = addArgs->begin();
1394 a != addArgs->end(); ++a)
1395 Args.push_back(a->c_str());
1396 if (Comp == false && FileName.empty() == false)
1397 {
1398 // commands not needing arguments, do not need to be told about using standard output
1399 // in reality, only testcases with tools like cat, rev, rot13, … are able to trigger this
1400 if (compressor.CompressArgs.empty() == false && compressor.UncompressArgs.empty() == false)
1401 Args.push_back("--stdout");
1402 if (TemporaryFileName.empty() == false)
1403 Args.push_back(TemporaryFileName.c_str());
1404 else
1405 Args.push_back(FileName.c_str());
1406 }
1407 Args.push_back(NULL);
1408
1409 execvp(Args[0],(char **)&Args[0]);
1410 cerr << _("Failed to exec compressor ") << Args[0] << endl;
1411 _exit(100);
1412 }
1413 if (Comp == true)
1414 close(Pipe[0]);
1415 else
1416 close(Pipe[1]);
1417
1418 return true;
1419 }
1420 /*}}}*/
1421 // FileFd::~File - Closes the file /*{{{*/
1422 // ---------------------------------------------------------------------
1423 /* If the proper modes are selected then we close the Fd and possibly
1424 unlink the file on error. */
1425 FileFd::~FileFd()
1426 {
1427 Close();
1428 if (d != NULL)
1429 d->CloseDown(FileName);
1430 delete d;
1431 d = NULL;
1432 }
1433 /*}}}*/
1434 // FileFd::Read - Read a bit of the file /*{{{*/
1435 // ---------------------------------------------------------------------
1436 /* We are careful to handle interruption by a signal while reading
1437 gracefully. */
1438 bool FileFd::Read(void *To,unsigned long long Size,unsigned long long *Actual)
1439 {
1440 ssize_t Res;
1441 errno = 0;
1442 if (Actual != 0)
1443 *Actual = 0;
1444 *((char *)To) = '\0';
1445 do
1446 {
1447 if (false)
1448 /* dummy so that the rest can be 'else if's */;
1449 #ifdef HAVE_ZLIB
1450 else if (d != NULL && d->gz != NULL)
1451 Res = gzread(d->gz,To,Size);
1452 #endif
1453 #ifdef HAVE_BZ2
1454 else if (d != NULL && d->bz2 != NULL)
1455 Res = BZ2_bzread(d->bz2,To,Size);
1456 #endif
1457 #ifdef HAVE_LZMA
1458 else if (d != NULL && d->lzma != NULL)
1459 {
1460 if (d->lzma->eof == true)
1461 break;
1462
1463 d->lzma->stream.next_out = (uint8_t *) To;
1464 d->lzma->stream.avail_out = Size;
1465 if (d->lzma->stream.avail_in == 0)
1466 {
1467 d->lzma->stream.next_in = d->lzma->buffer;
1468 d->lzma->stream.avail_in = fread(d->lzma->buffer, 1, sizeof(d->lzma->buffer)/sizeof(d->lzma->buffer[0]), d->lzma->file);
1469 }
1470 d->lzma->err = lzma_code(&d->lzma->stream, LZMA_RUN);
1471 if (d->lzma->err == LZMA_STREAM_END)
1472 {
1473 d->lzma->eof = true;
1474 Res = Size - d->lzma->stream.avail_out;
1475 }
1476 else if (d->lzma->err != LZMA_OK)
1477 {
1478 Res = -1;
1479 errno = 0;
1480 }
1481 else
1482 {
1483 Res = Size - d->lzma->stream.avail_out;
1484 if (Res == 0)
1485 {
1486 // lzma run was okay, but produced no output…
1487 Res = -1;
1488 errno = EINTR;
1489 }
1490 }
1491 }
1492 #endif
1493 else
1494 Res = read(iFd,To,Size);
1495
1496 if (Res < 0)
1497 {
1498 if (errno == EINTR)
1499 {
1500 // trick the while-loop into running again
1501 Res = 1;
1502 errno = 0;
1503 continue;
1504 }
1505 if (false)
1506 /* dummy so that the rest can be 'else if's */;
1507 #ifdef HAVE_ZLIB
1508 else if (d != NULL && d->gz != NULL)
1509 {
1510 int err;
1511 char const * const errmsg = gzerror(d->gz, &err);
1512 if (err != Z_ERRNO)
1513 return FileFdError("gzread: %s (%d: %s)", _("Read error"), err, errmsg);
1514 }
1515 #endif
1516 #ifdef HAVE_BZ2
1517 else if (d != NULL && d->bz2 != NULL)
1518 {
1519 int err;
1520 char const * const errmsg = BZ2_bzerror(d->bz2, &err);
1521 if (err != BZ_IO_ERROR)
1522 return FileFdError("BZ2_bzread: %s (%d: %s)", _("Read error"), err, errmsg);
1523 }
1524 #endif
1525 #ifdef HAVE_LZMA
1526 else if (d != NULL && d->lzma != NULL)
1527 return FileFdError("lzma_read: %s (%d)", _("Read error"), d->lzma->err);
1528 #endif
1529 return FileFdErrno("read",_("Read error"));
1530 }
1531
1532 To = (char *)To + Res;
1533 Size -= Res;
1534 if (d != NULL)
1535 d->seekpos += Res;
1536 if (Actual != 0)
1537 *Actual += Res;
1538 }
1539 while (Res > 0 && Size > 0);
1540
1541 if (Size == 0)
1542 return true;
1543
1544 // Eof handling
1545 if (Actual != 0)
1546 {
1547 Flags |= HitEof;
1548 return true;
1549 }
1550
1551 return FileFdError(_("read, still have %llu to read but none left"), Size);
1552 }
1553 /*}}}*/
1554 // FileFd::ReadLine - Read a complete line from the file /*{{{*/
1555 // ---------------------------------------------------------------------
1556 /* Beware: This method can be quiet slow for big buffers on UNcompressed
1557 files because of the naive implementation! */
1558 char* FileFd::ReadLine(char *To, unsigned long long const Size)
1559 {
1560 *To = '\0';
1561 #ifdef HAVE_ZLIB
1562 if (d != NULL && d->gz != NULL)
1563 return gzgets(d->gz, To, Size);
1564 #endif
1565
1566 unsigned long long read = 0;
1567 while ((Size - 1) != read)
1568 {
1569 unsigned long long done = 0;
1570 if (Read(To + read, 1, &done) == false)
1571 return NULL;
1572 if (done == 0)
1573 break;
1574 if (To[read++] == '\n')
1575 break;
1576 }
1577 if (read == 0)
1578 return NULL;
1579 To[read] = '\0';
1580 return To;
1581 }
1582 /*}}}*/
1583 // FileFd::Write - Write to the file /*{{{*/
1584 // ---------------------------------------------------------------------
1585 /* */
1586 bool FileFd::Write(const void *From,unsigned long long Size)
1587 {
1588 ssize_t Res;
1589 errno = 0;
1590 do
1591 {
1592 if (false)
1593 /* dummy so that the rest can be 'else if's */;
1594 #ifdef HAVE_ZLIB
1595 else if (d != NULL && d->gz != NULL)
1596 Res = gzwrite(d->gz,From,Size);
1597 #endif
1598 #ifdef HAVE_BZ2
1599 else if (d != NULL && d->bz2 != NULL)
1600 Res = BZ2_bzwrite(d->bz2,(void*)From,Size);
1601 #endif
1602 #ifdef HAVE_LZMA
1603 else if (d != NULL && d->lzma != NULL)
1604 {
1605 d->lzma->stream.next_in = (uint8_t *)From;
1606 d->lzma->stream.avail_in = Size;
1607 d->lzma->stream.next_out = d->lzma->buffer;
1608 d->lzma->stream.avail_out = sizeof(d->lzma->buffer)/sizeof(d->lzma->buffer[0]);
1609 d->lzma->err = lzma_code(&d->lzma->stream, LZMA_RUN);
1610 if (d->lzma->err != LZMA_OK)
1611 return false;
1612 size_t const n = sizeof(d->lzma->buffer)/sizeof(d->lzma->buffer[0]) - d->lzma->stream.avail_out;
1613 size_t const m = (n == 0) ? 0 : fwrite(d->lzma->buffer, 1, n, d->lzma->file);
1614 if (m != n)
1615 Res = -1;
1616 else
1617 Res = Size - d->lzma->stream.avail_in;
1618 }
1619 #endif
1620 else
1621 Res = write(iFd,From,Size);
1622
1623 if (Res < 0 && errno == EINTR)
1624 continue;
1625 if (Res < 0)
1626 {
1627 if (false)
1628 /* dummy so that the rest can be 'else if's */;
1629 #ifdef HAVE_ZLIB
1630 else if (d != NULL && d->gz != NULL)
1631 {
1632 int err;
1633 char const * const errmsg = gzerror(d->gz, &err);
1634 if (err != Z_ERRNO)
1635 return FileFdError("gzwrite: %s (%d: %s)", _("Write error"), err, errmsg);
1636 }
1637 #endif
1638 #ifdef HAVE_BZ2
1639 else if (d != NULL && d->bz2 != NULL)
1640 {
1641 int err;
1642 char const * const errmsg = BZ2_bzerror(d->bz2, &err);
1643 if (err != BZ_IO_ERROR)
1644 return FileFdError("BZ2_bzwrite: %s (%d: %s)", _("Write error"), err, errmsg);
1645 }
1646 #endif
1647 #ifdef HAVE_LZMA
1648 else if (d != NULL && d->lzma != NULL)
1649 return FileFdErrno("lzma_fwrite", _("Write error"));
1650 #endif
1651 return FileFdErrno("write",_("Write error"));
1652 }
1653
1654 From = (char const *)From + Res;
1655 Size -= Res;
1656 if (d != NULL)
1657 d->seekpos += Res;
1658 }
1659 while (Res > 0 && Size > 0);
1660
1661 if (Size == 0)
1662 return true;
1663
1664 return FileFdError(_("write, still have %llu to write but couldn't"), Size);
1665 }
1666 bool FileFd::Write(int Fd, const void *From, unsigned long long Size)
1667 {
1668 ssize_t Res;
1669 errno = 0;
1670 do
1671 {
1672 Res = write(Fd,From,Size);
1673 if (Res < 0 && errno == EINTR)
1674 continue;
1675 if (Res < 0)
1676 return _error->Errno("write",_("Write error"));
1677
1678 From = (char const *)From + Res;
1679 Size -= Res;
1680 }
1681 while (Res > 0 && Size > 0);
1682
1683 if (Size == 0)
1684 return true;
1685
1686 return _error->Error(_("write, still have %llu to write but couldn't"), Size);
1687 }
1688 /*}}}*/
1689 // FileFd::Seek - Seek in the file /*{{{*/
1690 // ---------------------------------------------------------------------
1691 /* */
1692 bool FileFd::Seek(unsigned long long To)
1693 {
1694 Flags &= ~HitEof;
1695
1696 if (d != NULL && (d->pipe == true || d->InternalStream() == true))
1697 {
1698 // Our poor man seeking in pipes is costly, so try to avoid it
1699 unsigned long long seekpos = Tell();
1700 if (seekpos == To)
1701 return true;
1702 else if (seekpos < To)
1703 return Skip(To - seekpos);
1704
1705 if ((d->openmode & ReadOnly) != ReadOnly)
1706 return FileFdError("Reopen is only implemented for read-only files!");
1707 d->InternalClose(FileName);
1708 if (iFd != -1)
1709 close(iFd);
1710 iFd = -1;
1711 if (TemporaryFileName.empty() == false)
1712 iFd = open(TemporaryFileName.c_str(), O_RDONLY);
1713 else if (FileName.empty() == false)
1714 iFd = open(FileName.c_str(), O_RDONLY);
1715 else
1716 {
1717 if (d->compressed_fd > 0)
1718 if (lseek(d->compressed_fd, 0, SEEK_SET) != 0)
1719 iFd = d->compressed_fd;
1720 if (iFd < 0)
1721 return FileFdError("Reopen is not implemented for pipes opened with FileFd::OpenDescriptor()!");
1722 }
1723
1724 if (OpenInternDescriptor(d->openmode, d->compressor) == false)
1725 return FileFdError("Seek on file %s because it couldn't be reopened", FileName.c_str());
1726
1727 if (To != 0)
1728 return Skip(To);
1729
1730 d->seekpos = To;
1731 return true;
1732 }
1733 off_t res;
1734 #ifdef HAVE_ZLIB
1735 if (d != NULL && d->gz)
1736 res = gzseek(d->gz,To,SEEK_SET);
1737 else
1738 #endif
1739 res = lseek(iFd,To,SEEK_SET);
1740 if (res != (off_t)To)
1741 return FileFdError("Unable to seek to %llu", To);
1742
1743 if (d != NULL)
1744 d->seekpos = To;
1745 return true;
1746 }
1747 /*}}}*/
1748 // FileFd::Skip - Seek in the file /*{{{*/
1749 // ---------------------------------------------------------------------
1750 /* */
1751 bool FileFd::Skip(unsigned long long Over)
1752 {
1753 if (d != NULL && (d->pipe == true || d->InternalStream() == true))
1754 {
1755 char buffer[1024];
1756 while (Over != 0)
1757 {
1758 unsigned long long toread = std::min((unsigned long long) sizeof(buffer), Over);
1759 if (Read(buffer, toread) == false)
1760 return FileFdError("Unable to seek ahead %llu",Over);
1761 Over -= toread;
1762 }
1763 return true;
1764 }
1765
1766 off_t res;
1767 #ifdef HAVE_ZLIB
1768 if (d != NULL && d->gz != NULL)
1769 res = gzseek(d->gz,Over,SEEK_CUR);
1770 else
1771 #endif
1772 res = lseek(iFd,Over,SEEK_CUR);
1773 if (res < 0)
1774 return FileFdError("Unable to seek ahead %llu",Over);
1775 if (d != NULL)
1776 d->seekpos = res;
1777
1778 return true;
1779 }
1780 /*}}}*/
1781 // FileFd::Truncate - Truncate the file /*{{{*/
1782 // ---------------------------------------------------------------------
1783 /* */
1784 bool FileFd::Truncate(unsigned long long To)
1785 {
1786 // truncating /dev/null is always successful - as we get an error otherwise
1787 if (To == 0 && FileName == "/dev/null")
1788 return true;
1789 #if defined HAVE_ZLIB || defined HAVE_BZ2 || defined HAVE_LZMA
1790 if (d != NULL && (d->InternalStream() == true
1791 #ifdef HAVE_ZLIB
1792 || d->gz != NULL
1793 #endif
1794 ))
1795 return FileFdError("Truncating compressed files is not implemented (%s)", FileName.c_str());
1796 #endif
1797 if (ftruncate(iFd,To) != 0)
1798 return FileFdError("Unable to truncate to %llu",To);
1799
1800 return true;
1801 }
1802 /*}}}*/
1803 // FileFd::Tell - Current seek position /*{{{*/
1804 // ---------------------------------------------------------------------
1805 /* */
1806 unsigned long long FileFd::Tell()
1807 {
1808 // In theory, we could just return seekpos here always instead of
1809 // seeking around, but not all users of FileFd use always Seek() and co
1810 // so d->seekpos isn't always true and we can just use it as a hint if
1811 // we have nothing else, but not always as an authority…
1812 if (d != NULL && (d->pipe == true || d->InternalStream() == true))
1813 return d->seekpos;
1814
1815 off_t Res;
1816 #ifdef HAVE_ZLIB
1817 if (d != NULL && d->gz != NULL)
1818 Res = gztell(d->gz);
1819 else
1820 #endif
1821 Res = lseek(iFd,0,SEEK_CUR);
1822 if (Res == (off_t)-1)
1823 FileFdErrno("lseek","Failed to determine the current file position");
1824 if (d != NULL)
1825 d->seekpos = Res;
1826 return Res;
1827 }
1828 /*}}}*/
1829 static bool StatFileFd(char const * const msg, int const iFd, std::string const &FileName, struct stat &Buf, FileFdPrivate * const d) /*{{{*/
1830 {
1831 bool ispipe = (d != NULL && d->pipe == true);
1832 if (ispipe == false)
1833 {
1834 if (fstat(iFd,&Buf) != 0)
1835 // higher-level code will generate more meaningful messages,
1836 // even translated this would be meaningless for users
1837 return _error->Errno("fstat", "Unable to determine %s for fd %i", msg, iFd);
1838 if (FileName.empty() == false)
1839 ispipe = S_ISFIFO(Buf.st_mode);
1840 }
1841
1842 // for compressor pipes st_size is undefined and at 'best' zero
1843 if (ispipe == true)
1844 {
1845 // we set it here, too, as we get the info here for free
1846 // in theory the Open-methods should take care of it already
1847 if (d != NULL)
1848 d->pipe = true;
1849 if (stat(FileName.c_str(), &Buf) != 0)
1850 return _error->Errno("fstat", "Unable to determine %s for file %s", msg, FileName.c_str());
1851 }
1852 return true;
1853 }
1854 /*}}}*/
1855 // FileFd::FileSize - Return the size of the file /*{{{*/
1856 unsigned long long FileFd::FileSize()
1857 {
1858 struct stat Buf;
1859 if (StatFileFd("file size", iFd, FileName, Buf, d) == false)
1860 {
1861 Flags |= Fail;
1862 return 0;
1863 }
1864 return Buf.st_size;
1865 }
1866 /*}}}*/
1867 // FileFd::ModificationTime - Return the time of last touch /*{{{*/
1868 time_t FileFd::ModificationTime()
1869 {
1870 struct stat Buf;
1871 if (StatFileFd("modification time", iFd, FileName, Buf, d) == false)
1872 {
1873 Flags |= Fail;
1874 return 0;
1875 }
1876 return Buf.st_mtime;
1877 }
1878 /*}}}*/
1879 // FileFd::Size - Return the size of the content in the file /*{{{*/
1880 // ---------------------------------------------------------------------
1881 /* */
1882 unsigned long long FileFd::Size()
1883 {
1884 unsigned long long size = FileSize();
1885
1886 // for compressor pipes st_size is undefined and at 'best' zero,
1887 // so we 'read' the content and 'seek' back - see there
1888 if (d != NULL && (d->pipe == true || (d->InternalStream() == true && size > 0)))
1889 {
1890 unsigned long long const oldSeek = Tell();
1891 char ignore[1000];
1892 unsigned long long read = 0;
1893 do {
1894 if (Read(ignore, sizeof(ignore), &read) == false)
1895 {
1896 Seek(oldSeek);
1897 return 0;
1898 }
1899 } while(read != 0);
1900 size = Tell();
1901 Seek(oldSeek);
1902 }
1903 #ifdef HAVE_ZLIB
1904 // only check gzsize if we are actually a gzip file, just checking for
1905 // "gz" is not sufficient as uncompressed files could be opened with
1906 // gzopen in "direct" mode as well
1907 else if (d != NULL && d->gz && !gzdirect(d->gz) && size > 0)
1908 {
1909 off_t const oldPos = lseek(iFd,0,SEEK_CUR);
1910 /* unfortunately zlib.h doesn't provide a gzsize(), so we have to do
1911 * this ourselves; the original (uncompressed) file size is the last 32
1912 * bits of the file */
1913 // FIXME: Size for gz-files is limited by 32bit… no largefile support
1914 if (lseek(iFd, -4, SEEK_END) < 0)
1915 {
1916 FileFdErrno("lseek","Unable to seek to end of gzipped file");
1917 return 0;
1918 }
1919 uint32_t size = 0;
1920 if (read(iFd, &size, 4) != 4)
1921 {
1922 FileFdErrno("read","Unable to read original size of gzipped file");
1923 return 0;
1924 }
1925 size = le32toh(size);
1926
1927 if (lseek(iFd, oldPos, SEEK_SET) < 0)
1928 {
1929 FileFdErrno("lseek","Unable to seek in gzipped file");
1930 return 0;
1931 }
1932
1933 return size;
1934 }
1935 #endif
1936
1937 return size;
1938 }
1939 /*}}}*/
1940 // FileFd::Close - Close the file if the close flag is set /*{{{*/
1941 // ---------------------------------------------------------------------
1942 /* */
1943 bool FileFd::Close()
1944 {
1945 if (iFd == -1)
1946 return true;
1947
1948 bool Res = true;
1949 if ((Flags & AutoClose) == AutoClose)
1950 {
1951 if ((Flags & Compressed) != Compressed && iFd > 0 && close(iFd) != 0)
1952 Res &= _error->Errno("close",_("Problem closing the file %s"), FileName.c_str());
1953 if (d != NULL)
1954 {
1955 Res &= d->CloseDown(FileName);
1956 delete d;
1957 d = NULL;
1958 }
1959 }
1960
1961 if ((Flags & Replace) == Replace) {
1962 if (rename(TemporaryFileName.c_str(), FileName.c_str()) != 0)
1963 Res &= _error->Errno("rename",_("Problem renaming the file %s to %s"), TemporaryFileName.c_str(), FileName.c_str());
1964
1965 FileName = TemporaryFileName; // for the unlink() below.
1966 TemporaryFileName.clear();
1967 }
1968
1969 iFd = -1;
1970
1971 if ((Flags & Fail) == Fail && (Flags & DelOnFail) == DelOnFail &&
1972 FileName.empty() == false)
1973 if (unlink(FileName.c_str()) != 0)
1974 Res &= _error->WarningE("unlnk",_("Problem unlinking the file %s"), FileName.c_str());
1975
1976 if (Res == false)
1977 Flags |= Fail;
1978 return Res;
1979 }
1980 /*}}}*/
1981 // FileFd::Sync - Sync the file /*{{{*/
1982 // ---------------------------------------------------------------------
1983 /* */
1984 bool FileFd::Sync()
1985 {
1986 if (fsync(iFd) != 0)
1987 return FileFdErrno("sync",_("Problem syncing the file"));
1988 return true;
1989 }
1990 /*}}}*/
1991 // FileFd::FileFdErrno - set Fail and call _error->Errno *{{{*/
1992 bool FileFd::FileFdErrno(const char *Function, const char *Description,...)
1993 {
1994 Flags |= Fail;
1995 va_list args;
1996 size_t msgSize = 400;
1997 int const errsv = errno;
1998 while (true)
1999 {
2000 va_start(args,Description);
2001 if (_error->InsertErrno(GlobalError::ERROR, Function, Description, args, errsv, msgSize) == false)
2002 break;
2003 va_end(args);
2004 }
2005 return false;
2006 }
2007 /*}}}*/
2008 // FileFd::FileFdError - set Fail and call _error->Error *{{{*/
2009 bool FileFd::FileFdError(const char *Description,...) {
2010 Flags |= Fail;
2011 va_list args;
2012 size_t msgSize = 400;
2013 while (true)
2014 {
2015 va_start(args,Description);
2016 if (_error->Insert(GlobalError::ERROR, Description, args, msgSize) == false)
2017 break;
2018 va_end(args);
2019 }
2020 return false;
2021 }
2022 /*}}}*/
2023
2024 APT_DEPRECATED gzFile FileFd::gzFd() {
2025 #ifdef HAVE_ZLIB
2026 return d->gz;
2027 #else
2028 return NULL;
2029 #endif
2030 }
2031
2032
2033 // Glob - wrapper around "glob()" /*{{{*/
2034 // ---------------------------------------------------------------------
2035 /* */
2036 std::vector<std::string> Glob(std::string const &pattern, int flags)
2037 {
2038 std::vector<std::string> result;
2039 glob_t globbuf;
2040 int glob_res;
2041 unsigned int i;
2042
2043 glob_res = glob(pattern.c_str(), flags, NULL, &globbuf);
2044
2045 if (glob_res != 0)
2046 {
2047 if(glob_res != GLOB_NOMATCH) {
2048 _error->Errno("glob", "Problem with glob");
2049 return result;
2050 }
2051 }
2052
2053 // append results
2054 for(i=0;i<globbuf.gl_pathc;i++)
2055 result.push_back(string(globbuf.gl_pathv[i]));
2056
2057 globfree(&globbuf);
2058 return result;
2059 }
2060 /*}}}*/
2061
2062 std::string GetTempDir()
2063 {
2064 const char *tmpdir = getenv("TMPDIR");
2065
2066 #ifdef P_tmpdir
2067 if (!tmpdir)
2068 tmpdir = P_tmpdir;
2069 #endif
2070
2071 // check that tmpdir is set and exists
2072 struct stat st;
2073 if (!tmpdir || strlen(tmpdir) == 0 || stat(tmpdir, &st) != 0)
2074 tmpdir = "/tmp";
2075
2076 return string(tmpdir);
2077 }
2078
2079 FileFd* GetTempFile(std::string const &Prefix, bool ImmediateUnlink)
2080 {
2081 char fn[512];
2082 FileFd *Fd = new FileFd();
2083
2084 std::string tempdir = GetTempDir();
2085 snprintf(fn, sizeof(fn), "%s/%s.XXXXXX",
2086 tempdir.c_str(), Prefix.c_str());
2087 int fd = mkstemp(fn);
2088 if(ImmediateUnlink)
2089 unlink(fn);
2090 if (fd < 0)
2091 {
2092 _error->Errno("GetTempFile",_("Unable to mkstemp %s"), fn);
2093 return NULL;
2094 }
2095 if (!Fd->OpenDescriptor(fd, FileFd::WriteOnly, FileFd::None, true))
2096 {
2097 _error->Errno("GetTempFile",_("Unable to write to %s"),fn);
2098 return NULL;
2099 }
2100
2101 return Fd;
2102 }
2103
2104 bool Rename(std::string From, std::string To)
2105 {
2106 if (rename(From.c_str(),To.c_str()) != 0)
2107 {
2108 _error->Error(_("rename failed, %s (%s -> %s)."),strerror(errno),
2109 From.c_str(),To.c_str());
2110 return false;
2111 }
2112 return true;
2113 }
2114
2115 bool Popen(const char* Args[], FileFd &Fd, pid_t &Child, FileFd::OpenMode Mode)
2116 {
2117 int fd;
2118 if (Mode != FileFd::ReadOnly && Mode != FileFd::WriteOnly)
2119 return _error->Error("Popen supports ReadOnly (x)or WriteOnly mode only");
2120
2121 int Pipe[2] = {-1, -1};
2122 if(pipe(Pipe) != 0)
2123 {
2124 return _error->Errno("pipe", _("Failed to create subprocess IPC"));
2125 return NULL;
2126 }
2127 std::set<int> keep_fds;
2128 keep_fds.insert(Pipe[0]);
2129 keep_fds.insert(Pipe[1]);
2130 Child = ExecFork(keep_fds);
2131 if(Child < 0)
2132 return _error->Errno("fork", "Failed to fork");
2133 if(Child == 0)
2134 {
2135 if(Mode == FileFd::ReadOnly)
2136 {
2137 close(Pipe[0]);
2138 fd = Pipe[1];
2139 }
2140 else if(Mode == FileFd::WriteOnly)
2141 {
2142 close(Pipe[1]);
2143 fd = Pipe[0];
2144 }
2145
2146 if(Mode == FileFd::ReadOnly)
2147 {
2148 dup2(fd, 1);
2149 dup2(fd, 2);
2150 } else if(Mode == FileFd::WriteOnly)
2151 dup2(fd, 0);
2152
2153 execv(Args[0], (char**)Args);
2154 _exit(100);
2155 }
2156 if(Mode == FileFd::ReadOnly)
2157 {
2158 close(Pipe[1]);
2159 fd = Pipe[0];
2160 } else if(Mode == FileFd::WriteOnly)
2161 {
2162 close(Pipe[0]);
2163 fd = Pipe[1];
2164 }
2165 Fd.OpenDescriptor(fd, Mode, FileFd::None, true);
2166
2167 return true;
2168 }