dpkgpm.cc 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407
  1. // -*- mode: cpp; mode: fold -*-
  2. // Description /*{{{*/
  3. // $Id: dpkgpm.cc,v 1.13 1999/07/30 06:15:14 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[5];
  159. Args[0] = "/bin/sh";
  160. Args[1] = "/bin/sh";
  161. Args[2] = "-c";
  162. Args[3] = Opts->Value.c_str();
  163. Args[4] = 0;
  164. execv(Args[0],(char **)Args);
  165. _exit(100);
  166. }
  167. close(Pipes[0]);
  168. FileFd Fd(Pipes[1]);
  169. // Feed it the filenames.
  170. for (vector<Item>::iterator I = List.begin(); I != List.end();)
  171. {
  172. // Only deal with packages to be installed from .deb
  173. if (I->Op != Item::Install)
  174. continue;
  175. // No errors here..
  176. if (I->File[0] != '/')
  177. continue;
  178. /* Feed the filename of each package that is pending install
  179. into the pipe. */
  180. if (Fd.Write(I->File.begin(),I->File.length()) == false ||
  181. Fd.Write("\n",1) == false)
  182. {
  183. kill(Process,SIGINT);
  184. Fd.Close();
  185. ExecWait(Process,Opts->Value.c_str(),true);
  186. return false;
  187. }
  188. }
  189. Fd.Close();
  190. // Clean up the sub process
  191. if (ExecWait(Process,Opts->Value.c_str()) == false)
  192. return false;
  193. }
  194. return true;
  195. }
  196. /*}}}*/
  197. // DPkgPM::Go - Run the sequence /*{{{*/
  198. // ---------------------------------------------------------------------
  199. /* This globs the operations and calls dpkg */
  200. bool pkgDPkgPM::Go()
  201. {
  202. if (RunScripts("DPkg::Pre-Invoke") == false)
  203. return false;
  204. if (RunScriptsWithPkgs("DPkg::Pre-Install-Pkgs") == false)
  205. return false;
  206. for (vector<Item>::iterator I = List.begin(); I != List.end();)
  207. {
  208. vector<Item>::iterator J = I;
  209. for (; J != List.end() && J->Op == I->Op; J++);
  210. // Generate the argument list
  211. const char *Args[400];
  212. if (J - I > 350)
  213. J = I + 350;
  214. unsigned int n = 0;
  215. unsigned long Size = 0;
  216. Args[n++] = _config->Find("Dir::Bin::dpkg","dpkg").c_str();
  217. Size += strlen(Args[n-1]);
  218. // Stick in any custom dpkg options
  219. Configuration::Item const *Opts = _config->Tree("DPkg::Options");
  220. if (Opts != 0)
  221. {
  222. Opts = Opts->Child;
  223. for (; Opts != 0; Opts = Opts->Next)
  224. {
  225. if (Opts->Value.empty() == true)
  226. continue;
  227. Args[n++] = Opts->Value.c_str();
  228. Size += Opts->Value.length();
  229. }
  230. }
  231. switch (I->Op)
  232. {
  233. case Item::Remove:
  234. Args[n++] = "--force-depends";
  235. Size += strlen(Args[n-1]);
  236. Args[n++] = "--force-remove-essential";
  237. Size += strlen(Args[n-1]);
  238. Args[n++] = "--remove";
  239. Size += strlen(Args[n-1]);
  240. break;
  241. case Item::Purge:
  242. Args[n++] = "--force-depends";
  243. Size += strlen(Args[n-1]);
  244. Args[n++] = "--force-remove-essential";
  245. Size += strlen(Args[n-1]);
  246. Args[n++] = "--purge";
  247. Size += strlen(Args[n-1]);
  248. break;
  249. case Item::Configure:
  250. Args[n++] = "--configure";
  251. Size += strlen(Args[n-1]);
  252. break;
  253. case Item::Install:
  254. Args[n++] = "--unpack";
  255. Size += strlen(Args[n-1]);
  256. break;
  257. }
  258. // Write in the file or package names
  259. if (I->Op == Item::Install)
  260. {
  261. for (;I != J && Size < 1024; I++)
  262. {
  263. if (I->File[0] != '/')
  264. return _error->Error("Internal Error, Pathname to install is not absolute '%s'",I->File.c_str());
  265. Args[n++] = I->File.c_str();
  266. Size += strlen(Args[n-1]);
  267. }
  268. }
  269. else
  270. {
  271. for (;I != J && Size < 1024; I++)
  272. {
  273. Args[n++] = I->Pkg.Name();
  274. Size += strlen(Args[n-1]);
  275. }
  276. }
  277. Args[n] = 0;
  278. J = I;
  279. if (_config->FindB("Debug::pkgDPkgPM",false) == true)
  280. {
  281. for (unsigned int k = 0; k != n; k++)
  282. clog << Args[k] << ' ';
  283. clog << endl;
  284. continue;
  285. }
  286. cout << flush;
  287. clog << flush;
  288. cerr << flush;
  289. /* Mask off sig int/quit. We do this because dpkg also does when
  290. it forks scripts. What happens is that when you hit ctrl-c it sends
  291. it to all processes in the group. Since dpkg ignores the signal
  292. it doesn't die but we do! So we must also ignore it */
  293. signal(SIGQUIT,SIG_IGN);
  294. signal(SIGINT,SIG_IGN);
  295. // Fork dpkg
  296. pid_t Child = ExecFork();
  297. // This is the child
  298. if (Child == 0)
  299. {
  300. if (chdir(_config->FindDir("DPkg::Run-Directory","/").c_str()) != 0)
  301. _exit(100);
  302. int Flags,dummy;
  303. if ((Flags = fcntl(STDIN_FILENO,F_GETFL,dummy)) < 0)
  304. _exit(100);
  305. // Discard everything in stdin before forking dpkg
  306. if (fcntl(STDIN_FILENO,F_SETFL,Flags | O_NONBLOCK) < 0)
  307. _exit(100);
  308. while (read(STDIN_FILENO,&dummy,1) == 1);
  309. if (fcntl(STDIN_FILENO,F_SETFL,Flags & (~(long)O_NONBLOCK)) < 0)
  310. _exit(100);
  311. /* No Job Control Stop Env is a magic dpkg var that prevents it
  312. from using sigstop */
  313. setenv("DPKG_NO_TSTP","yes",1);
  314. execvp(Args[0],(char **)Args);
  315. cerr << "Could not exec dpkg!" << endl;
  316. _exit(100);
  317. }
  318. // Wait for dpkg
  319. int Status = 0;
  320. while (waitpid(Child,&Status,0) != Child)
  321. {
  322. if (errno == EINTR)
  323. continue;
  324. RunScripts("DPkg::Post-Invoke");
  325. return _error->Errno("waitpid","Couldn't wait for subprocess");
  326. }
  327. // Restore sig int/quit
  328. signal(SIGQUIT,SIG_DFL);
  329. signal(SIGINT,SIG_DFL);
  330. // Check for an error code.
  331. if (WIFEXITED(Status) == 0 || WEXITSTATUS(Status) != 0)
  332. {
  333. RunScripts("DPkg::Post-Invoke");
  334. if (WIFSIGNALED(Status) != 0 && WTERMSIG(Status) == SIGSEGV)
  335. return _error->Error("Sub-process recieved a segmentation fault.");
  336. if (WIFEXITED(Status) != 0)
  337. return _error->Error("Sub-process returned an error code (%u)",WEXITSTATUS(Status));
  338. return _error->Error("Sub-process exited unexpectedly");
  339. }
  340. }
  341. if (RunScripts("DPkg::Post-Invoke") == false)
  342. return false;
  343. return true;
  344. }
  345. /*}}}*/
  346. // pkgDpkgPM::Reset - Dump the contents of the command list /*{{{*/
  347. // ---------------------------------------------------------------------
  348. /* */
  349. void pkgDPkgPM::Reset()
  350. {
  351. List.erase(List.begin(),List.end());
  352. }
  353. /*}}}*/