* apt-pkg/contrib/fileutl.cc, apt-pkg/deb/dpkgpm.cc:
[ntk/apt.git] / apt-pkg / contrib / fileutl.cc
CommitLineData
578bfd0a
AL
1// -*- mode: cpp; mode: fold -*-
2// Description /*{{{*/
7da2b375 3// $Id: fileutl.cc,v 1.42 2002/09/14 05:29:22 jgg Exp $
578bfd0a
AL
4/* ######################################################################
5
6 File Utilities
7
8 CopyFile - Buffered copy of a single file
9 GetLock - dpkg compatible lock file manipulation (fcntl)
10
614adaa0
MV
11 Most of this source is placed in the Public Domain, do with it what
12 you will
7da2b375 13 It was originally written by Jason Gunthorpe <jgg@debian.org>.
a3a03f5d 14 FileFd gzip support added by Martin Pitt <martin.pitt@canonical.com>
578bfd0a 15
614adaa0
MV
16 The exception is RunScripts() it is under the GPLv2
17
578bfd0a
AL
18 ##################################################################### */
19 /*}}}*/
20// Include Files /*{{{*/
094a497d 21#include <apt-pkg/fileutl.h>
1cd1c398 22#include <apt-pkg/strutl.h>
094a497d 23#include <apt-pkg/error.h>
b2e465d6 24#include <apt-pkg/sptr.h>
75ef8f14 25#include <apt-pkg/configuration.h>
b2e465d6
AL
26
27#include <apti18n.h>
578bfd0a 28
152ab79e 29#include <cstdlib>
4f333a8b 30#include <cstring>
3010fb0e 31#include <cstdio>
4f333a8b 32
4d055c05 33#include <iostream>
578bfd0a 34#include <unistd.h>
2c206aa4 35#include <fcntl.h>
578bfd0a 36#include <sys/stat.h>
578bfd0a 37#include <sys/types.h>
cc2313b7 38#include <sys/time.h>
1ae93c94 39#include <sys/wait.h>
46e39c8e 40#include <dirent.h>
54676e1a 41#include <signal.h>
65a1e968 42#include <errno.h>
75ef8f14 43#include <set>
46e39c8e 44#include <algorithm>
578bfd0a
AL
45 /*}}}*/
46
4d055c05
AL
47using namespace std;
48
614adaa0
MV
49// RunScripts - Run a set of scripts from a configuration subtree /*{{{*/
50// ---------------------------------------------------------------------
51/* */
52bool RunScripts(const char *Cnf)
53{
54 Configuration::Item const *Opts = _config->Tree(Cnf);
55 if (Opts == 0 || Opts->Child == 0)
56 return true;
57 Opts = Opts->Child;
58
59 // Fork for running the system calls
60 pid_t Child = ExecFork();
61
62 // This is the child
63 if (Child == 0)
64 {
cfba4f69
MV
65 if (_config->FindDir("DPkg::Chroot-Directory","/") != "/")
66 {
67 std::cerr << "Chrooting into "
68 << _config->FindDir("DPkg::Chroot-Directory")
69 << std::endl;
70 if (chroot(_config->FindDir("DPkg::Chroot-Directory","/").c_str()) != 0)
71 _exit(100);
72 }
73
614adaa0
MV
74 if (chdir("/tmp/") != 0)
75 _exit(100);
76
77 unsigned int Count = 1;
78 for (; Opts != 0; Opts = Opts->Next, Count++)
79 {
80 if (Opts->Value.empty() == true)
81 continue;
82
83 if (system(Opts->Value.c_str()) != 0)
84 _exit(100+Count);
85 }
86 _exit(0);
87 }
88
89 // Wait for the child
90 int Status = 0;
91 while (waitpid(Child,&Status,0) != Child)
92 {
93 if (errno == EINTR)
94 continue;
95 return _error->Errno("waitpid","Couldn't wait for subprocess");
96 }
97
98 // Restore sig int/quit
99 signal(SIGQUIT,SIG_DFL);
100 signal(SIGINT,SIG_DFL);
101
102 // Check for an error code.
103 if (WIFEXITED(Status) == 0 || WEXITSTATUS(Status) != 0)
104 {
105 unsigned int Count = WEXITSTATUS(Status);
106 if (Count > 100)
107 {
108 Count -= 100;
109 for (; Opts != 0 && Count != 1; Opts = Opts->Next, Count--);
110 _error->Error("Problem executing scripts %s '%s'",Cnf,Opts->Value.c_str());
111 }
112
113 return _error->Error("Sub-process returned an error code");
114 }
115
116 return true;
117}
118 /*}}}*/
119
578bfd0a
AL
120// CopyFile - Buffered copy of a file /*{{{*/
121// ---------------------------------------------------------------------
122/* The caller is expected to set things so that failure causes erasure */
8b89e57f 123bool CopyFile(FileFd &From,FileFd &To)
578bfd0a
AL
124{
125 if (From.IsOpen() == false || To.IsOpen() == false)
126 return false;
127
128 // Buffered copy between fds
b2e465d6 129 SPtrArray<unsigned char> Buf = new unsigned char[64000];
b0db36b1
AL
130 unsigned long Size = From.Size();
131 while (Size != 0)
578bfd0a 132 {
b0db36b1
AL
133 unsigned long ToRead = Size;
134 if (Size > 64000)
135 ToRead = 64000;
136
4a6d5862 137 if (From.Read(Buf,ToRead) == false ||
b0db36b1 138 To.Write(Buf,ToRead) == false)
578bfd0a 139 return false;
b0db36b1
AL
140
141 Size -= ToRead;
578bfd0a
AL
142 }
143
578bfd0a
AL
144 return true;
145}
146 /*}}}*/
147// GetLock - Gets a lock file /*{{{*/
148// ---------------------------------------------------------------------
149/* This will create an empty file of the given name and lock it. Once this
150 is done all other calls to GetLock in any other process will fail with
151 -1. The return result is the fd of the file, the call should call
152 close at some time. */
153int GetLock(string File,bool Errors)
154{
f659b39a
OS
155 // GetLock() is used in aptitude on directories with public-write access
156 // Use O_NOFOLLOW here to prevent symlink traversal attacks
157 int FD = open(File.c_str(),O_RDWR | O_CREAT | O_NOFOLLOW,0640);
578bfd0a
AL
158 if (FD < 0)
159 {
b2e465d6
AL
160 // Read only .. cant have locking problems there.
161 if (errno == EROFS)
162 {
163 _error->Warning(_("Not using locking for read only lock file %s"),File.c_str());
164 return dup(0); // Need something for the caller to close
165 }
166
578bfd0a 167 if (Errors == true)
b2e465d6
AL
168 _error->Errno("open",_("Could not open lock file %s"),File.c_str());
169
170 // Feh.. We do this to distinguish the lock vs open case..
171 errno = EPERM;
578bfd0a
AL
172 return -1;
173 }
b2e465d6
AL
174 SetCloseExec(FD,true);
175
578bfd0a
AL
176 // Aquire a write lock
177 struct flock fl;
c71bc556
AL
178 fl.l_type = F_WRLCK;
179 fl.l_whence = SEEK_SET;
180 fl.l_start = 0;
181 fl.l_len = 0;
578bfd0a
AL
182 if (fcntl(FD,F_SETLK,&fl) == -1)
183 {
d89df07a
AL
184 if (errno == ENOLCK)
185 {
b2e465d6
AL
186 _error->Warning(_("Not using locking for nfs mounted lock file %s"),File.c_str());
187 return dup(0); // Need something for the caller to close
d89df07a 188 }
578bfd0a 189 if (Errors == true)
b2e465d6
AL
190 _error->Errno("open",_("Could not get lock %s"),File.c_str());
191
192 int Tmp = errno;
578bfd0a 193 close(FD);
b2e465d6 194 errno = Tmp;
578bfd0a
AL
195 return -1;
196 }
197
198 return FD;
199}
200 /*}}}*/
201// FileExists - Check if a file exists /*{{{*/
202// ---------------------------------------------------------------------
36f1098a 203/* Beware: Directories are also files! */
578bfd0a
AL
204bool FileExists(string File)
205{
206 struct stat Buf;
207 if (stat(File.c_str(),&Buf) != 0)
208 return false;
209 return true;
210}
211 /*}}}*/
36f1098a
DK
212// RealFileExists - Check if a file exists and if it is really a file /*{{{*/
213// ---------------------------------------------------------------------
214/* */
215bool RealFileExists(string File)
216{
217 struct stat Buf;
218 if (stat(File.c_str(),&Buf) != 0)
219 return false;
220 return ((Buf.st_mode & S_IFREG) != 0);
221}
222 /*}}}*/
1cd1c398
DK
223// DirectoryExists - Check if a directory exists and is really one /*{{{*/
224// ---------------------------------------------------------------------
225/* */
226bool DirectoryExists(string const &Path)
227{
228 struct stat Buf;
229 if (stat(Path.c_str(),&Buf) != 0)
230 return false;
231 return ((Buf.st_mode & S_IFDIR) != 0);
232}
233 /*}}}*/
234// CreateDirectory - poor man's mkdir -p guarded by a parent directory /*{{{*/
235// ---------------------------------------------------------------------
236/* This method will create all directories needed for path in good old
237 mkdir -p style but refuses to do this if Parent is not a prefix of
238 this Path. Example: /var/cache/ and /var/cache/apt/archives are given,
239 so it will create apt/archives if /var/cache exists - on the other
240 hand if the parent is /var/lib the creation will fail as this path
241 is not a parent of the path to be generated. */
242bool CreateDirectory(string const &Parent, string const &Path)
243{
244 if (Parent.empty() == true || Path.empty() == true)
245 return false;
246
247 if (DirectoryExists(Path) == true)
248 return true;
249
250 if (DirectoryExists(Parent) == false)
251 return false;
252
253 // we are not going to create directories "into the blue"
254 if (Path.find(Parent, 0) != 0)
255 return false;
256
257 vector<string> const dirs = VectorizeString(Path.substr(Parent.size()), '/');
258 string progress = Parent;
259 for (vector<string>::const_iterator d = dirs.begin(); d != dirs.end(); ++d)
260 {
261 if (d->empty() == true)
262 continue;
263
264 progress.append("/").append(*d);
265 if (DirectoryExists(progress) == true)
266 continue;
267
268 if (mkdir(progress.c_str(), 0755) != 0)
269 return false;
270 }
271 return true;
272}
273 /*}}}*/
7753e468 274// CreateAPTDirectoryIfNeeded - ensure that the given directory exists /*{{{*/
b29c3712
DK
275// ---------------------------------------------------------------------
276/* a small wrapper around CreateDirectory to check if it exists and to
277 remove the trailing "/apt/" from the parent directory if needed */
7753e468 278bool CreateAPTDirectoryIfNeeded(string const &Parent, string const &Path)
b29c3712
DK
279{
280 if (DirectoryExists(Path) == true)
281 return true;
282
283 size_t const len = Parent.size();
284 if (len > 5 && Parent.find("/apt/", len - 6, 5) == len - 5)
285 {
286 if (CreateDirectory(Parent.substr(0,len-5), Path) == true)
287 return true;
288 }
289 else if (CreateDirectory(Parent, Path) == true)
290 return true;
291
292 return false;
293}
294 /*}}}*/
46e39c8e
MV
295// GetListOfFilesInDir - returns a vector of files in the given dir /*{{{*/
296// ---------------------------------------------------------------------
297/* If an extension is given only files with this extension are included
298 in the returned vector, otherwise every "normal" file is included. */
b39c1859
MV
299std::vector<string> GetListOfFilesInDir(string const &Dir, string const &Ext,
300 bool const &SortList, bool const &AllowNoExt)
301{
302 std::vector<string> ext;
303 ext.reserve(2);
304 if (Ext.empty() == false)
305 ext.push_back(Ext);
306 if (AllowNoExt == true && ext.empty() == false)
307 ext.push_back("");
308 return GetListOfFilesInDir(Dir, ext, SortList);
309}
310std::vector<string> GetListOfFilesInDir(string const &Dir, std::vector<string> const &Ext,
311 bool const &SortList)
312{
313 // Attention debuggers: need to be set with the environment config file!
314 bool const Debug = _config->FindB("Debug::GetListOfFilesInDir", false);
315 if (Debug == true)
316 {
317 std::clog << "Accept in " << Dir << " only files with the following " << Ext.size() << " extensions:" << std::endl;
318 if (Ext.empty() == true)
319 std::clog << "\tNO extension" << std::endl;
320 else
321 for (std::vector<string>::const_iterator e = Ext.begin();
322 e != Ext.end(); ++e)
323 std::clog << '\t' << (e->empty() == true ? "NO" : *e) << " extension" << std::endl;
324 }
325
46e39c8e 326 std::vector<string> List;
36f1098a
DK
327
328 if (DirectoryExists(Dir.c_str()) == false)
329 {
330 _error->Error(_("List of files can't be created as '%s' is not a directory"), Dir.c_str());
331 return List;
332 }
333
1408e219 334 Configuration::MatchAgainstConfig SilentIgnore("Dir::Ignore-Files-Silently");
46e39c8e
MV
335 DIR *D = opendir(Dir.c_str());
336 if (D == 0)
337 {
338 _error->Errno("opendir",_("Unable to read %s"),Dir.c_str());
339 return List;
340 }
341
342 for (struct dirent *Ent = readdir(D); Ent != 0; Ent = readdir(D))
343 {
b39c1859 344 // skip "hidden" files
46e39c8e
MV
345 if (Ent->d_name[0] == '.')
346 continue;
347
491058e3
DK
348 // Make sure it is a file and not something else
349 string const File = flCombine(Dir,Ent->d_name);
350#ifdef _DIRENT_HAVE_D_TYPE
351 if (Ent->d_type != DT_REG)
352#endif
353 {
354 if (RealFileExists(File.c_str()) == false)
355 {
356 if (SilentIgnore.Match(Ent->d_name) == false)
357 _error->Notice(_("Ignoring '%s' in directory '%s' as it is not a regular file"), Ent->d_name, Dir.c_str());
358 continue;
359 }
360 }
361
b39c1859
MV
362 // check for accepted extension:
363 // no extension given -> periods are bad as hell!
364 // extensions given -> "" extension allows no extension
365 if (Ext.empty() == false)
366 {
367 string d_ext = flExtension(Ent->d_name);
368 if (d_ext == Ent->d_name) // no extension
369 {
370 if (std::find(Ext.begin(), Ext.end(), "") == Ext.end())
371 {
372 if (Debug == true)
373 std::clog << "Bad file: " << Ent->d_name << " → no extension" << std::endl;
5edc3966 374 if (SilentIgnore.Match(Ent->d_name) == false)
491058e3 375 _error->Notice(_("Ignoring file '%s' in directory '%s' as it has no filename extension"), Ent->d_name, Dir.c_str());
b39c1859
MV
376 continue;
377 }
378 }
379 else if (std::find(Ext.begin(), Ext.end(), d_ext) == Ext.end())
380 {
381 if (Debug == true)
382 std::clog << "Bad file: " << Ent->d_name << " → bad extension »" << flExtension(Ent->d_name) << "«" << std::endl;
1408e219 383 if (SilentIgnore.Match(Ent->d_name) == false)
491058e3 384 _error->Notice(_("Ignoring file '%s' in directory '%s' as it has an invalid filename extension"), Ent->d_name, Dir.c_str());
b39c1859
MV
385 continue;
386 }
387 }
46e39c8e 388
b39c1859 389 // Skip bad filenames ala run-parts
46e39c8e
MV
390 const char *C = Ent->d_name;
391 for (; *C != 0; ++C)
392 if (isalpha(*C) == 0 && isdigit(*C) == 0
b39c1859
MV
393 && *C != '_' && *C != '-') {
394 // no required extension -> dot is a bad character
395 if (*C == '.' && Ext.empty() == false)
396 continue;
46e39c8e 397 break;
b39c1859 398 }
46e39c8e 399
b39c1859 400 // we don't reach the end of the name -> bad character included
46e39c8e 401 if (*C != 0)
b39c1859
MV
402 {
403 if (Debug == true)
404 std::clog << "Bad file: " << Ent->d_name << " → bad character »"
405 << *C << "« in filename (period allowed: " << (Ext.empty() ? "no" : "yes") << ")" << std::endl;
406 continue;
407 }
408
409 // skip filenames which end with a period. These are never valid
410 if (*(C - 1) == '.')
411 {
412 if (Debug == true)
413 std::clog << "Bad file: " << Ent->d_name << " → Period as last character" << std::endl;
46e39c8e 414 continue;
b39c1859 415 }
46e39c8e 416
b39c1859
MV
417 if (Debug == true)
418 std::clog << "Accept file: " << Ent->d_name << " in " << Dir << std::endl;
46e39c8e
MV
419 List.push_back(File);
420 }
421 closedir(D);
422
423 if (SortList == true)
424 std::sort(List.begin(),List.end());
425 return List;
426}
427 /*}}}*/
578bfd0a
AL
428// SafeGetCWD - This is a safer getcwd that returns a dynamic string /*{{{*/
429// ---------------------------------------------------------------------
430/* We return / on failure. */
431string SafeGetCWD()
432{
433 // Stash the current dir.
434 char S[300];
435 S[0] = 0;
7f25bdff 436 if (getcwd(S,sizeof(S)-2) == 0)
578bfd0a 437 return "/";
7f25bdff
AL
438 unsigned int Len = strlen(S);
439 S[Len] = '/';
440 S[Len+1] = 0;
578bfd0a
AL
441 return S;
442}
443 /*}}}*/
8ce4327b
AL
444// flNotDir - Strip the directory from the filename /*{{{*/
445// ---------------------------------------------------------------------
446/* */
447string flNotDir(string File)
448{
449 string::size_type Res = File.rfind('/');
450 if (Res == string::npos)
451 return File;
452 Res++;
453 return string(File,Res,Res - File.length());
454}
455 /*}}}*/
d38b7b3d
AL
456// flNotFile - Strip the file from the directory name /*{{{*/
457// ---------------------------------------------------------------------
171c45bc 458/* Result ends in a / */
d38b7b3d
AL
459string flNotFile(string File)
460{
461 string::size_type Res = File.rfind('/');
462 if (Res == string::npos)
171c45bc 463 return "./";
d38b7b3d
AL
464 Res++;
465 return string(File,0,Res);
466}
467 /*}}}*/
b2e465d6
AL
468// flExtension - Return the extension for the file /*{{{*/
469// ---------------------------------------------------------------------
470/* */
471string flExtension(string File)
472{
473 string::size_type Res = File.rfind('.');
474 if (Res == string::npos)
475 return File;
476 Res++;
477 return string(File,Res,Res - File.length());
478}
479 /*}}}*/
421c8d10
AL
480// flNoLink - If file is a symlink then deref it /*{{{*/
481// ---------------------------------------------------------------------
482/* If the name is not a link then the returned path is the input. */
483string flNoLink(string File)
484{
485 struct stat St;
486 if (lstat(File.c_str(),&St) != 0 || S_ISLNK(St.st_mode) == 0)
487 return File;
488 if (stat(File.c_str(),&St) != 0)
489 return File;
490
491 /* Loop resolving the link. There is no need to limit the number of
492 loops because the stat call above ensures that the symlink is not
493 circular */
494 char Buffer[1024];
495 string NFile = File;
496 while (1)
497 {
498 // Read the link
499 int Res;
500 if ((Res = readlink(NFile.c_str(),Buffer,sizeof(Buffer))) <= 0 ||
501 (unsigned)Res >= sizeof(Buffer))
502 return File;
503
504 // Append or replace the previous path
505 Buffer[Res] = 0;
506 if (Buffer[0] == '/')
507 NFile = Buffer;
508 else
509 NFile = flNotFile(NFile) + Buffer;
510
511 // See if we are done
512 if (lstat(NFile.c_str(),&St) != 0)
513 return File;
514 if (S_ISLNK(St.st_mode) == 0)
515 return NFile;
516 }
517}
518 /*}}}*/
b2e465d6
AL
519// flCombine - Combine a file and a directory /*{{{*/
520// ---------------------------------------------------------------------
521/* If the file is an absolute path then it is just returned, otherwise
522 the directory is pre-pended to it. */
523string flCombine(string Dir,string File)
524{
525 if (File.empty() == true)
526 return string();
527
528 if (File[0] == '/' || Dir.empty() == true)
529 return File;
530 if (File.length() >= 2 && File[0] == '.' && File[1] == '/')
531 return File;
532 if (Dir[Dir.length()-1] == '/')
533 return Dir + File;
534 return Dir + '/' + File;
535}
536 /*}}}*/
3b5421b4
AL
537// SetCloseExec - Set the close on exec flag /*{{{*/
538// ---------------------------------------------------------------------
539/* */
540void SetCloseExec(int Fd,bool Close)
541{
542 if (fcntl(Fd,F_SETFD,(Close == false)?0:FD_CLOEXEC) != 0)
543 {
544 cerr << "FATAL -> Could not set close on exec " << strerror(errno) << endl;
545 exit(100);
546 }
547}
548 /*}}}*/
549// SetNonBlock - Set the nonblocking flag /*{{{*/
550// ---------------------------------------------------------------------
551/* */
552void SetNonBlock(int Fd,bool Block)
553{
0a8a80e5
AL
554 int Flags = fcntl(Fd,F_GETFL) & (~O_NONBLOCK);
555 if (fcntl(Fd,F_SETFL,Flags | ((Block == false)?0:O_NONBLOCK)) != 0)
3b5421b4
AL
556 {
557 cerr << "FATAL -> Could not set non-blocking flag " << strerror(errno) << endl;
558 exit(100);
559 }
560}
561 /*}}}*/
562// WaitFd - Wait for a FD to become readable /*{{{*/
563// ---------------------------------------------------------------------
b2e465d6 564/* This waits for a FD to become readable using select. It is useful for
6d5dd02a
AL
565 applications making use of non-blocking sockets. The timeout is
566 in seconds. */
1084d58a 567bool WaitFd(int Fd,bool write,unsigned long timeout)
3b5421b4
AL
568{
569 fd_set Set;
cc2313b7 570 struct timeval tv;
3b5421b4
AL
571 FD_ZERO(&Set);
572 FD_SET(Fd,&Set);
6d5dd02a
AL
573 tv.tv_sec = timeout;
574 tv.tv_usec = 0;
1084d58a 575 if (write == true)
b0db36b1
AL
576 {
577 int Res;
578 do
579 {
580 Res = select(Fd+1,0,&Set,0,(timeout != 0?&tv:0));
581 }
582 while (Res < 0 && errno == EINTR);
583
584 if (Res <= 0)
585 return false;
1084d58a
AL
586 }
587 else
588 {
b0db36b1
AL
589 int Res;
590 do
591 {
592 Res = select(Fd+1,&Set,0,0,(timeout != 0?&tv:0));
593 }
594 while (Res < 0 && errno == EINTR);
595
596 if (Res <= 0)
597 return false;
cc2313b7 598 }
1084d58a 599
3b5421b4
AL
600 return true;
601}
602 /*}}}*/
54676e1a
AL
603// ExecFork - Magical fork that sanitizes the context before execing /*{{{*/
604// ---------------------------------------------------------------------
605/* This is used if you want to cleanse the environment for the forked
606 child, it fixes up the important signals and nukes all of the fds,
607 otherwise acts like normal fork. */
75ef8f14 608pid_t ExecFork()
54676e1a
AL
609{
610 // Fork off the process
611 pid_t Process = fork();
612 if (Process < 0)
613 {
614 cerr << "FATAL -> Failed to fork." << endl;
615 exit(100);
616 }
617
618 // Spawn the subprocess
619 if (Process == 0)
620 {
621 // Setup the signals
622 signal(SIGPIPE,SIG_DFL);
623 signal(SIGQUIT,SIG_DFL);
624 signal(SIGINT,SIG_DFL);
625 signal(SIGWINCH,SIG_DFL);
626 signal(SIGCONT,SIG_DFL);
627 signal(SIGTSTP,SIG_DFL);
75ef8f14
MV
628
629 set<int> KeepFDs;
630 Configuration::Item const *Opts = _config->Tree("APT::Keep-Fds");
631 if (Opts != 0 && Opts->Child != 0)
632 {
633 Opts = Opts->Child;
634 for (; Opts != 0; Opts = Opts->Next)
635 {
636 if (Opts->Value.empty() == true)
637 continue;
638 int fd = atoi(Opts->Value.c_str());
639 KeepFDs.insert(fd);
640 }
641 }
642
54676e1a
AL
643 // Close all of our FDs - just in case
644 for (int K = 3; K != 40; K++)
75ef8f14
MV
645 {
646 if(KeepFDs.find(K) == KeepFDs.end())
007dc9e0 647 fcntl(K,F_SETFD,FD_CLOEXEC);
75ef8f14 648 }
54676e1a
AL
649 }
650
651 return Process;
652}
653 /*}}}*/
ddc1d8d0
AL
654// ExecWait - Fancy waitpid /*{{{*/
655// ---------------------------------------------------------------------
2c9a72d1 656/* Waits for the given sub process. If Reap is set then no errors are
ddc1d8d0
AL
657 generated. Otherwise a failed subprocess will generate a proper descriptive
658 message */
3826564e 659bool ExecWait(pid_t Pid,const char *Name,bool Reap)
ddc1d8d0
AL
660{
661 if (Pid <= 1)
662 return true;
663
664 // Wait and collect the error code
665 int Status;
666 while (waitpid(Pid,&Status,0) != Pid)
667 {
668 if (errno == EINTR)
669 continue;
670
671 if (Reap == true)
672 return false;
673
db0db9fe 674 return _error->Error(_("Waited for %s but it wasn't there"),Name);
ddc1d8d0
AL
675 }
676
677
678 // Check for an error code.
679 if (WIFEXITED(Status) == 0 || WEXITSTATUS(Status) != 0)
680 {
681 if (Reap == true)
682 return false;
ab7f4d7c 683 if (WIFSIGNALED(Status) != 0)
40e7fe0e 684 {
ab7f4d7c
MV
685 if( WTERMSIG(Status) == SIGSEGV)
686 return _error->Error(_("Sub-process %s received a segmentation fault."),Name);
687 else
688 return _error->Error(_("Sub-process %s received signal %u."),Name, WTERMSIG(Status));
40e7fe0e 689 }
ddc1d8d0
AL
690
691 if (WIFEXITED(Status) != 0)
b2e465d6 692 return _error->Error(_("Sub-process %s returned an error code (%u)"),Name,WEXITSTATUS(Status));
ddc1d8d0 693
b2e465d6 694 return _error->Error(_("Sub-process %s exited unexpectedly"),Name);
ddc1d8d0
AL
695 }
696
697 return true;
698}
699 /*}}}*/
578bfd0a 700
13d87e2e 701// FileFd::Open - Open a file /*{{{*/
578bfd0a
AL
702// ---------------------------------------------------------------------
703/* The most commonly used open mode combinations are given with Mode */
13d87e2e 704bool FileFd::Open(string FileName,OpenMode Mode, unsigned long Perms)
578bfd0a 705{
13d87e2e 706 Close();
1164783d 707 Flags = AutoClose;
578bfd0a
AL
708 switch (Mode)
709 {
710 case ReadOnly:
711 iFd = open(FileName.c_str(),O_RDONLY);
712 break;
c4fc2fd7 713
714 case ReadOnlyGzip:
715 iFd = open(FileName.c_str(),O_RDONLY);
d13c2d3f 716 if (iFd > 0) {
c4fc2fd7 717 gz = gzdopen (iFd, "r");
718 if (gz == NULL) {
719 close (iFd);
720 iFd = -1;
721 }
a3a03f5d 722 }
578bfd0a
AL
723 break;
724
4a9db827 725 case WriteAtomic:
50b513a1 726 {
3010fb0e
JAK
727 Flags |= Replace;
728 char *name = strdup((FileName + ".XXXXXX").c_str());
729 TemporaryFileName = string(mktemp(name));
730 iFd = open(TemporaryFileName.c_str(),O_RDWR | O_CREAT | O_EXCL,Perms);
731 free(name);
50b513a1
AL
732 break;
733 }
4a9db827 734
fc81e8f2
JAK
735 case WriteEmpty:
736 {
737 struct stat Buf;
738 if (lstat(FileName.c_str(),&Buf) == 0 && S_ISLNK(Buf.st_mode))
739 unlink(FileName.c_str());
740 iFd = open(FileName.c_str(),O_RDWR | O_CREAT | O_TRUNC,Perms);
741 break;
742 }
578bfd0a
AL
743
744 case WriteExists:
745 iFd = open(FileName.c_str(),O_RDWR);
746 break;
0a8e3465
AL
747
748 case WriteAny:
749 iFd = open(FileName.c_str(),O_RDWR | O_CREAT,Perms);
d38b7b3d 750 break;
f08fcf34
AL
751
752 case WriteTemp:
4decd43c
AL
753 unlink(FileName.c_str());
754 iFd = open(FileName.c_str(),O_RDWR | O_CREAT | O_EXCL,Perms);
f08fcf34 755 break;
578bfd0a
AL
756 }
757
758 if (iFd < 0)
b2e465d6 759 return _error->Errno("open",_("Could not open file %s"),FileName.c_str());
13d87e2e
AL
760
761 this->FileName = FileName;
762 SetCloseExec(iFd,true);
763 return true;
578bfd0a 764}
144c0969
JAK
765
766bool FileFd::OpenDescriptor(int Fd, OpenMode Mode, bool AutoClose)
767{
768 Close();
769 Flags = (AutoClose) ? FileFd::AutoClose : 0;
770 iFd = Fd;
771 if (Mode == ReadOnlyGzip) {
772 gz = gzdopen (iFd, "r");
773 if (gz == NULL) {
774 if (AutoClose)
775 close (iFd);
776 return _error->Errno("gzdopen",_("Could not open file descriptor %d"),
777 Fd);
778 }
779 }
780 this->FileName = "";
781 return true;
782}
578bfd0a 783 /*}}}*/
8e06abb2 784// FileFd::~File - Closes the file /*{{{*/
578bfd0a
AL
785// ---------------------------------------------------------------------
786/* If the proper modes are selected then we close the Fd and possibly
787 unlink the file on error. */
8e06abb2 788FileFd::~FileFd()
578bfd0a
AL
789{
790 Close();
791}
792 /*}}}*/
8e06abb2 793// FileFd::Read - Read a bit of the file /*{{{*/
578bfd0a 794// ---------------------------------------------------------------------
b0db36b1
AL
795/* We are carefull to handle interruption by a signal while reading
796 gracefully. */
f604cf55 797bool FileFd::Read(void *To,unsigned long Size,unsigned long *Actual)
578bfd0a 798{
b0db36b1
AL
799 int Res;
800 errno = 0;
f604cf55
AL
801 if (Actual != 0)
802 *Actual = 0;
803
b0db36b1 804 do
578bfd0a 805 {
a3a03f5d 806 if (gz != NULL)
807 Res = gzread(gz,To,Size);
808 else
809 Res = read(iFd,To,Size);
b0db36b1
AL
810 if (Res < 0 && errno == EINTR)
811 continue;
812 if (Res < 0)
813 {
814 Flags |= Fail;
b2e465d6 815 return _error->Errno("read",_("Read error"));
b0db36b1 816 }
578bfd0a 817
b0db36b1
AL
818 To = (char *)To + Res;
819 Size -= Res;
f604cf55
AL
820 if (Actual != 0)
821 *Actual += Res;
b0db36b1
AL
822 }
823 while (Res > 0 && Size > 0);
824
825 if (Size == 0)
826 return true;
827
ddc1d8d0 828 // Eof handling
f604cf55 829 if (Actual != 0)
ddc1d8d0
AL
830 {
831 Flags |= HitEof;
832 return true;
833 }
834
b0db36b1 835 Flags |= Fail;
b2e465d6 836 return _error->Error(_("read, still have %lu to read but none left"),Size);
578bfd0a
AL
837}
838 /*}}}*/
8e06abb2 839// FileFd::Write - Write to the file /*{{{*/
578bfd0a
AL
840// ---------------------------------------------------------------------
841/* */
a05599f1 842bool FileFd::Write(const void *From,unsigned long Size)
578bfd0a 843{
b0db36b1
AL
844 int Res;
845 errno = 0;
846 do
578bfd0a 847 {
a3a03f5d 848 if (gz != NULL)
849 Res = gzwrite(gz,From,Size);
850 else
851 Res = write(iFd,From,Size);
b0db36b1
AL
852 if (Res < 0 && errno == EINTR)
853 continue;
854 if (Res < 0)
855 {
856 Flags |= Fail;
b2e465d6 857 return _error->Errno("write",_("Write error"));
b0db36b1
AL
858 }
859
860 From = (char *)From + Res;
861 Size -= Res;
578bfd0a 862 }
b0db36b1 863 while (Res > 0 && Size > 0);
578bfd0a 864
b0db36b1
AL
865 if (Size == 0)
866 return true;
867
868 Flags |= Fail;
b2e465d6 869 return _error->Error(_("write, still have %lu to write but couldn't"),Size);
578bfd0a
AL
870}
871 /*}}}*/
8e06abb2 872// FileFd::Seek - Seek in the file /*{{{*/
578bfd0a
AL
873// ---------------------------------------------------------------------
874/* */
8e06abb2 875bool FileFd::Seek(unsigned long To)
578bfd0a 876{
a3a03f5d 877 int res;
878 if (gz)
879 res = gzseek(gz,To,SEEK_SET);
880 else
881 res = lseek(iFd,To,SEEK_SET);
882 if (res != (signed)To)
578bfd0a
AL
883 {
884 Flags |= Fail;
b2e465d6 885 return _error->Error("Unable to seek to %lu",To);
578bfd0a
AL
886 }
887
727f18af
AL
888 return true;
889}
890 /*}}}*/
891// FileFd::Skip - Seek in the file /*{{{*/
892// ---------------------------------------------------------------------
893/* */
894bool FileFd::Skip(unsigned long Over)
895{
a3a03f5d 896 int res;
897 if (gz)
898 res = gzseek(gz,Over,SEEK_CUR);
899 else
900 res = lseek(iFd,Over,SEEK_CUR);
901 if (res < 0)
727f18af
AL
902 {
903 Flags |= Fail;
b2e465d6 904 return _error->Error("Unable to seek ahead %lu",Over);
727f18af
AL
905 }
906
6d5dd02a
AL
907 return true;
908}
909 /*}}}*/
910// FileFd::Truncate - Truncate the file /*{{{*/
911// ---------------------------------------------------------------------
912/* */
913bool FileFd::Truncate(unsigned long To)
914{
a3a03f5d 915 if (gz)
916 {
917 Flags |= Fail;
918 return _error->Error("Truncating gzipped files is not implemented (%s)", FileName.c_str());
919 }
6d5dd02a
AL
920 if (ftruncate(iFd,To) != 0)
921 {
922 Flags |= Fail;
b2e465d6 923 return _error->Error("Unable to truncate to %lu",To);
6d5dd02a
AL
924 }
925
578bfd0a
AL
926 return true;
927}
928 /*}}}*/
7f25bdff
AL
929// FileFd::Tell - Current seek position /*{{{*/
930// ---------------------------------------------------------------------
931/* */
932unsigned long FileFd::Tell()
933{
a3a03f5d 934 off_t Res;
935 if (gz)
936 Res = gztell(gz);
937 else
938 Res = lseek(iFd,0,SEEK_CUR);
7f25bdff
AL
939 if (Res == (off_t)-1)
940 _error->Errno("lseek","Failed to determine the current file position");
941 return Res;
942}
943 /*}}}*/
4260fd39 944// FileFd::FileSize - Return the size of the file /*{{{*/
578bfd0a
AL
945// ---------------------------------------------------------------------
946/* */
4260fd39 947unsigned long FileFd::FileSize()
578bfd0a
AL
948{
949 struct stat Buf;
9c182afa 950
44dc669e
MV
951 if (fstat(iFd,&Buf) != 0)
952 return _error->Errno("fstat","Unable to determine the file size");
4260fd39
DK
953 return Buf.st_size;
954}
955 /*}}}*/
956// FileFd::Size - Return the size of the content in the file /*{{{*/
957// ---------------------------------------------------------------------
958/* */
959unsigned long FileFd::Size()
960{
961 unsigned long size = FileSize();
44dc669e
MV
962
963 // only check gzsize if we are actually a gzip file, just checking for
964 // "gz" is not sufficient as uncompressed files will be opened with
965 // gzopen in "direct" mode as well
966 if (gz && !gzdirect(gz) && size > 0)
9c182afa
MP
967 {
968 /* unfortunately zlib.h doesn't provide a gzsize(), so we have to do
969 * this ourselves; the original (uncompressed) file size is the last 32
970 * bits of the file */
4260fd39 971 off_t orig_pos = lseek(iFd, 0, SEEK_CUR);
9c182afa
MP
972 if (lseek(iFd, -4, SEEK_END) < 0)
973 return _error->Errno("lseek","Unable to seek to end of gzipped file");
974 if (read(iFd, &size, 4) != 4)
975 return _error->Errno("read","Unable to read original size of gzipped file");
976 size &= 0xFFFFFFFF;
977
978 if (lseek(iFd, orig_pos, SEEK_SET) < 0)
979 return _error->Errno("lseek","Unable to seek in gzipped file");
980 return size;
981 }
982
44dc669e 983 return size;
578bfd0a
AL
984}
985 /*}}}*/
8e06abb2 986// FileFd::Close - Close the file if the close flag is set /*{{{*/
578bfd0a
AL
987// ---------------------------------------------------------------------
988/* */
8e06abb2 989bool FileFd::Close()
578bfd0a
AL
990{
991 bool Res = true;
992 if ((Flags & AutoClose) == AutoClose)
d13c2d3f 993 {
994 if (gz != NULL) {
3184b4cf 995 int const e = gzclose(gz);
d13c2d3f 996 // gzdopen() on empty files always fails with "buffer error" here, ignore that
997 if (e != 0 && e != Z_BUF_ERROR)
3184b4cf 998 Res &= _error->Errno("close",_("Problem closing the gzip file %s"), FileName.c_str());
d13c2d3f 999 } else
1000 if (iFd > 0 && close(iFd) != 0)
3184b4cf 1001 Res &= _error->Errno("close",_("Problem closing the file %s"), FileName.c_str());
d13c2d3f 1002 }
3010fb0e 1003
62d073d9 1004 if ((Flags & Replace) == Replace && iFd >= 0) {
3010fb0e 1005 if (rename(TemporaryFileName.c_str(), FileName.c_str()) != 0)
62d073d9
DK
1006 Res &= _error->Errno("rename",_("Problem renaming the file %s to %s"), TemporaryFileName.c_str(), FileName.c_str());
1007
fd3b761e 1008 FileName = TemporaryFileName; // for the unlink() below.
3010fb0e 1009 }
62d073d9
DK
1010
1011 iFd = -1;
a3a03f5d 1012 gz = NULL;
62d073d9 1013
578bfd0a
AL
1014 if ((Flags & Fail) == Fail && (Flags & DelOnFail) == DelOnFail &&
1015 FileName.empty() == false)
1016 if (unlink(FileName.c_str()) != 0)
62d073d9 1017 Res &= _error->WarningE("unlnk",_("Problem unlinking the file %s"), FileName.c_str());
3010fb0e
JAK
1018
1019
578bfd0a
AL
1020 return Res;
1021}
1022 /*}}}*/
b2e465d6
AL
1023// FileFd::Sync - Sync the file /*{{{*/
1024// ---------------------------------------------------------------------
1025/* */
1026bool FileFd::Sync()
1027{
1028#ifdef _POSIX_SYNCHRONIZED_IO
1029 if (fsync(iFd) != 0)
1030 return _error->Errno("sync",_("Problem syncing the file"));
1031#endif
1032 return true;
1033}
1034 /*}}}*/