dpkgpm.cc 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406
  1. // -*- mode: cpp; mode: fold -*-
  2. // Description /*{{{*/
  3. // $Id: dpkgpm.cc,v 1.14 1999/09/10 02:40:31 jgg Exp $
  4. /* ######################################################################
  5. DPKG Package Manager - Provide an interface to dpkg
  6. ##################################################################### */
  7. /*}}}*/
  8. // Includes /*{{{*/
  9. #ifdef __GNUG__
  10. #pragma implementation "apt-pkg/dpkgpm.h"
  11. #endif
  12. #include <apt-pkg/dpkgpm.h>
  13. #include <apt-pkg/error.h>
  14. #include <apt-pkg/configuration.h>
  15. #include <unistd.h>
  16. #include <stdlib.h>
  17. #include <fcntl.h>
  18. #include <sys/types.h>
  19. #include <sys/wait.h>
  20. #include <signal.h>
  21. #include <errno.h>
  22. #include <stdio.h>
  23. /*}}}*/
  24. // DPkgPM::pkgDPkgPM - Constructor /*{{{*/
  25. // ---------------------------------------------------------------------
  26. /* */
  27. pkgDPkgPM::pkgDPkgPM(pkgDepCache &Cache) : pkgPackageManager(Cache)
  28. {
  29. }
  30. /*}}}*/
  31. // DPkgPM::pkgDPkgPM - Destructor /*{{{*/
  32. // ---------------------------------------------------------------------
  33. /* */
  34. pkgDPkgPM::~pkgDPkgPM()
  35. {
  36. }
  37. /*}}}*/
  38. // DPkgPM::Install - Install a package /*{{{*/
  39. // ---------------------------------------------------------------------
  40. /* Add an install operation to the sequence list */
  41. bool pkgDPkgPM::Install(PkgIterator Pkg,string File)
  42. {
  43. if (File.empty() == true || Pkg.end() == true)
  44. return _error->Error("Internal Error, No file name for %s",Pkg.Name());
  45. List.push_back(Item(Item::Install,Pkg,File));
  46. return true;
  47. }
  48. /*}}}*/
  49. // DPkgPM::Configure - Configure a package /*{{{*/
  50. // ---------------------------------------------------------------------
  51. /* Add a configure operation to the sequence list */
  52. bool pkgDPkgPM::Configure(PkgIterator Pkg)
  53. {
  54. if (Pkg.end() == true)
  55. return false;
  56. List.push_back(Item(Item::Configure,Pkg));
  57. return true;
  58. }
  59. /*}}}*/
  60. // DPkgPM::Remove - Remove a package /*{{{*/
  61. // ---------------------------------------------------------------------
  62. /* Add a remove operation to the sequence list */
  63. bool pkgDPkgPM::Remove(PkgIterator Pkg,bool Purge)
  64. {
  65. if (Pkg.end() == true)
  66. return false;
  67. if (Purge == true)
  68. List.push_back(Item(Item::Purge,Pkg));
  69. else
  70. List.push_back(Item(Item::Remove,Pkg));
  71. return true;
  72. }
  73. /*}}}*/
  74. // DPkgPM::RunScripts - Run a set of scripts /*{{{*/
  75. // ---------------------------------------------------------------------
  76. /* This looks for a list of script sto run from the configuration file,
  77. each one is run with system from a forked child. */
  78. bool pkgDPkgPM::RunScripts(const char *Cnf)
  79. {
  80. Configuration::Item const *Opts = _config->Tree(Cnf);
  81. if (Opts == 0 || Opts->Child == 0)
  82. return true;
  83. Opts = Opts->Child;
  84. // Fork for running the system calls
  85. pid_t Child = ExecFork();
  86. // This is the child
  87. if (Child == 0)
  88. {
  89. if (chdir("/tmp/") != 0)
  90. _exit(100);
  91. unsigned int Count = 1;
  92. for (; Opts != 0; Opts = Opts->Next, Count++)
  93. {
  94. if (Opts->Value.empty() == true)
  95. continue;
  96. if (system(Opts->Value.c_str()) != 0)
  97. _exit(100+Count);
  98. }
  99. _exit(0);
  100. }
  101. // Wait for the child
  102. int Status = 0;
  103. while (waitpid(Child,&Status,0) != Child)
  104. {
  105. if (errno == EINTR)
  106. continue;
  107. return _error->Errno("waitpid","Couldn't wait for subprocess");
  108. }
  109. // Restore sig int/quit
  110. signal(SIGQUIT,SIG_DFL);
  111. signal(SIGINT,SIG_DFL);
  112. // Check for an error code.
  113. if (WIFEXITED(Status) == 0 || WEXITSTATUS(Status) != 0)
  114. {
  115. unsigned int Count = WEXITSTATUS(Status);
  116. if (Count > 100)
  117. {
  118. Count -= 100;
  119. for (; Opts != 0 && Count != 1; Opts = Opts->Next, Count--);
  120. _error->Error("Problem executing scripts %s '%s'",Cnf,Opts->Value.c_str());
  121. }
  122. return _error->Error("Sub-process returned an error code");
  123. }
  124. return true;
  125. }
  126. /*}}}*/
  127. // DPkgPM::RunScriptsWithPkgs - Run scripts with package names on stdin /*{{{*/
  128. // ---------------------------------------------------------------------
  129. /* This looks for a list of scripts to run from the configuration file
  130. each one is run and is fed on standard input a list of all .deb files
  131. that are due to be installed. */
  132. bool pkgDPkgPM::RunScriptsWithPkgs(const char *Cnf)
  133. {
  134. Configuration::Item const *Opts = _config->Tree(Cnf);
  135. if (Opts == 0 || Opts->Child == 0)
  136. return true;
  137. Opts = Opts->Child;
  138. unsigned int Count = 1;
  139. for (; Opts != 0; Opts = Opts->Next, Count++)
  140. {
  141. if (Opts->Value.empty() == true)
  142. continue;
  143. // Create the pipes
  144. int Pipes[2];
  145. if (pipe(Pipes) != 0)
  146. return _error->Errno("pipe","Failed to create IPC pipe to subprocess");
  147. SetCloseExec(Pipes[0],true);
  148. SetCloseExec(Pipes[1],true);
  149. // Purified Fork for running the script
  150. pid_t Process = ExecFork();
  151. if (Process == 0)
  152. {
  153. // Setup the FDs
  154. dup2(Pipes[0],STDIN_FILENO);
  155. SetCloseExec(STDOUT_FILENO,false);
  156. SetCloseExec(STDIN_FILENO,false);
  157. SetCloseExec(STDERR_FILENO,false);
  158. const char *Args[4];
  159. Args[0] = "/bin/sh";
  160. Args[1] = "-c";
  161. Args[2] = Opts->Value.c_str();
  162. Args[3] = 0;
  163. execv(Args[0],(char **)Args);
  164. _exit(100);
  165. }
  166. close(Pipes[0]);
  167. FileFd Fd(Pipes[1]);
  168. // Feed it the filenames.
  169. for (vector<Item>::iterator I = List.begin(); I != List.end(); I++)
  170. {
  171. // Only deal with packages to be installed from .deb
  172. if (I->Op != Item::Install)
  173. continue;
  174. // No errors here..
  175. if (I->File[0] != '/')
  176. continue;
  177. /* Feed the filename of each package that is pending install
  178. into the pipe. */
  179. if (Fd.Write(I->File.begin(),I->File.length()) == false ||
  180. Fd.Write("\n",1) == false)
  181. {
  182. kill(Process,SIGINT);
  183. Fd.Close();
  184. ExecWait(Process,Opts->Value.c_str(),true);
  185. return _error->Error("Failure running script %s",Opts->Value.c_str());
  186. }
  187. }
  188. Fd.Close();
  189. // Clean up the sub process
  190. if (ExecWait(Process,Opts->Value.c_str()) == false)
  191. return _error->Error("Failure running script %s",Opts->Value.c_str());
  192. }
  193. return true;
  194. }
  195. /*}}}*/
  196. // DPkgPM::Go - Run the sequence /*{{{*/
  197. // ---------------------------------------------------------------------
  198. /* This globs the operations and calls dpkg */
  199. bool pkgDPkgPM::Go()
  200. {
  201. if (RunScripts("DPkg::Pre-Invoke") == false)
  202. return false;
  203. if (RunScriptsWithPkgs("DPkg::Pre-Install-Pkgs") == false)
  204. return false;
  205. for (vector<Item>::iterator I = List.begin(); I != List.end();)
  206. {
  207. vector<Item>::iterator J = I;
  208. for (; J != List.end() && J->Op == I->Op; J++);
  209. // Generate the argument list
  210. const char *Args[400];
  211. if (J - I > 350)
  212. J = I + 350;
  213. unsigned int n = 0;
  214. unsigned long Size = 0;
  215. Args[n++] = _config->Find("Dir::Bin::dpkg","dpkg").c_str();
  216. Size += strlen(Args[n-1]);
  217. // Stick in any custom dpkg options
  218. Configuration::Item const *Opts = _config->Tree("DPkg::Options");
  219. if (Opts != 0)
  220. {
  221. Opts = Opts->Child;
  222. for (; Opts != 0; Opts = Opts->Next)
  223. {
  224. if (Opts->Value.empty() == true)
  225. continue;
  226. Args[n++] = Opts->Value.c_str();
  227. Size += Opts->Value.length();
  228. }
  229. }
  230. switch (I->Op)
  231. {
  232. case Item::Remove:
  233. Args[n++] = "--force-depends";
  234. Size += strlen(Args[n-1]);
  235. Args[n++] = "--force-remove-essential";
  236. Size += strlen(Args[n-1]);
  237. Args[n++] = "--remove";
  238. Size += strlen(Args[n-1]);
  239. break;
  240. case Item::Purge:
  241. Args[n++] = "--force-depends";
  242. Size += strlen(Args[n-1]);
  243. Args[n++] = "--force-remove-essential";
  244. Size += strlen(Args[n-1]);
  245. Args[n++] = "--purge";
  246. Size += strlen(Args[n-1]);
  247. break;
  248. case Item::Configure:
  249. Args[n++] = "--configure";
  250. Size += strlen(Args[n-1]);
  251. break;
  252. case Item::Install:
  253. Args[n++] = "--unpack";
  254. Size += strlen(Args[n-1]);
  255. break;
  256. }
  257. // Write in the file or package names
  258. if (I->Op == Item::Install)
  259. {
  260. for (;I != J && Size < 1024; I++)
  261. {
  262. if (I->File[0] != '/')
  263. return _error->Error("Internal Error, Pathname to install is not absolute '%s'",I->File.c_str());
  264. Args[n++] = I->File.c_str();
  265. Size += strlen(Args[n-1]);
  266. }
  267. }
  268. else
  269. {
  270. for (;I != J && Size < 1024; I++)
  271. {
  272. Args[n++] = I->Pkg.Name();
  273. Size += strlen(Args[n-1]);
  274. }
  275. }
  276. Args[n] = 0;
  277. J = I;
  278. if (_config->FindB("Debug::pkgDPkgPM",false) == true)
  279. {
  280. for (unsigned int k = 0; k != n; k++)
  281. clog << Args[k] << ' ';
  282. clog << endl;
  283. continue;
  284. }
  285. cout << flush;
  286. clog << flush;
  287. cerr << flush;
  288. /* Mask off sig int/quit. We do this because dpkg also does when
  289. it forks scripts. What happens is that when you hit ctrl-c it sends
  290. it to all processes in the group. Since dpkg ignores the signal
  291. it doesn't die but we do! So we must also ignore it */
  292. signal(SIGQUIT,SIG_IGN);
  293. signal(SIGINT,SIG_IGN);
  294. // Fork dpkg
  295. pid_t Child = ExecFork();
  296. // This is the child
  297. if (Child == 0)
  298. {
  299. if (chdir(_config->FindDir("DPkg::Run-Directory","/").c_str()) != 0)
  300. _exit(100);
  301. int Flags,dummy;
  302. if ((Flags = fcntl(STDIN_FILENO,F_GETFL,dummy)) < 0)
  303. _exit(100);
  304. // Discard everything in stdin before forking dpkg
  305. if (fcntl(STDIN_FILENO,F_SETFL,Flags | O_NONBLOCK) < 0)
  306. _exit(100);
  307. while (read(STDIN_FILENO,&dummy,1) == 1);
  308. if (fcntl(STDIN_FILENO,F_SETFL,Flags & (~(long)O_NONBLOCK)) < 0)
  309. _exit(100);
  310. /* No Job Control Stop Env is a magic dpkg var that prevents it
  311. from using sigstop */
  312. setenv("DPKG_NO_TSTP","yes",1);
  313. execvp(Args[0],(char **)Args);
  314. cerr << "Could not exec dpkg!" << endl;
  315. _exit(100);
  316. }
  317. // Wait for dpkg
  318. int Status = 0;
  319. while (waitpid(Child,&Status,0) != Child)
  320. {
  321. if (errno == EINTR)
  322. continue;
  323. RunScripts("DPkg::Post-Invoke");
  324. return _error->Errno("waitpid","Couldn't wait for subprocess");
  325. }
  326. // Restore sig int/quit
  327. signal(SIGQUIT,SIG_DFL);
  328. signal(SIGINT,SIG_DFL);
  329. // Check for an error code.
  330. if (WIFEXITED(Status) == 0 || WEXITSTATUS(Status) != 0)
  331. {
  332. RunScripts("DPkg::Post-Invoke");
  333. if (WIFSIGNALED(Status) != 0 && WTERMSIG(Status) == SIGSEGV)
  334. return _error->Error("Sub-process recieved a segmentation fault.");
  335. if (WIFEXITED(Status) != 0)
  336. return _error->Error("Sub-process returned an error code (%u)",WEXITSTATUS(Status));
  337. return _error->Error("Sub-process exited unexpectedly");
  338. }
  339. }
  340. if (RunScripts("DPkg::Post-Invoke") == false)
  341. return false;
  342. return true;
  343. }
  344. /*}}}*/
  345. // pkgDpkgPM::Reset - Dump the contents of the command list /*{{{*/
  346. // ---------------------------------------------------------------------
  347. /* */
  348. void pkgDPkgPM::Reset()
  349. {
  350. List.erase(List.begin(),List.end());
  351. }
  352. /*}}}*/