Mark installed packages as not automatically installed
[mspang/inapt.git] / inapt.cc
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <getopt.h>
4 #include <sys/utsname.h>
5 #include <iostream>
6 #include <cstdio>
7 #include <fstream>
8 #include <set>
9 #include <apt-pkg/pkgcache.h>
10 #include <apt-pkg/cachefile.h>
11 #include <apt-pkg/dpkgdb.h>
12 #include <apt-pkg/progress.h>
13 #include <apt-pkg/init.h>
14 #include <apt-pkg/error.h>
15 #include <apt-pkg/algorithms.h>
16 #include <apt-pkg/sptr.h>
17 #include <apt-pkg/acquire-item.h>
18
19 #include "inapt.h"
20 #include "util.h"
21 #include "acqprogress.h"
22
23 using namespace std;
24
25 char *prog = NULL;
26
27 static struct option opts[] = {
28     { NULL, 0, NULL, '\0' },
29 };
30
31 class AwesomeRootSetFunc : public pkgDepCache::InRootSetFunc {
32     std::set<std::string> root;
33
34     public:
35         AwesomeRootSetFunc(std::vector<inapt_package *> *final_actions) {
36             for (vector<inapt_package *>::iterator i = final_actions->begin(); i != final_actions->end(); i++)
37                 if ((*i)->action == inapt_action::INSTALL)
38                     root.insert((*i)->pkg.Name());
39         }
40         bool InRootSet(const pkgCache::PkgIterator &pkg) {
41 //            debug("irs %s %d", pkg.Name(), root.find(pkg.Name()) != root.end());
42             return root.find(pkg.Name()) != root.end();
43         }
44 };
45
46 bool run_install(pkgCacheFile &Cache,bool ShwKept = false,bool Ask = true,
47                      bool Safety = true)
48 {
49    if (_config->FindB("APT::Get::Purge", false) == true)
50    {
51       pkgCache::PkgIterator I = Cache->PkgBegin();
52       for (; I.end() == false; I++)
53       {
54          if (I.Purge() == false && Cache[I].Mode == pkgDepCache::ModeDelete)
55             Cache->MarkDelete(I, true);
56       }
57    }
58
59    if (Cache->BrokenCount() != 0)
60    {
61       return _error->Error("Internal error, run_install was called with broken packages!");
62    }
63
64    if (Cache->DelCount() == 0 && Cache->InstCount() == 0 &&
65        Cache->BadCount() == 0)
66       return true;
67
68    if (Cache->DelCount() != 0 && _config->FindB("APT::Get::Remove",true) == false)
69       return _error->Error(("Packages need to be removed but remove is disabled."));
70
71    pkgRecords Recs(Cache);
72    if (_error->PendingError() == true)
73       return false;
74
75    FileFd Lock;
76    if (_config->FindB("Debug::NoLocking",false) == false &&
77        _config->FindB("APT::Get::Print-URIs") == false)
78    {
79       Lock.Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
80       if (_error->PendingError() == true)
81          return _error->Error(("Unable to lock the download directory"));
82    }
83
84    unsigned int width = 80;
85    AcqTextStatus status (width, 0);
86    pkgAcquire Fetcher (&status);
87
88    pkgSourceList List;
89    if (List.ReadMainList() == false)
90       return _error->Error(("The list of sources could not be read."));
91
92    SPtr<pkgPackageManager> PM= _system->CreatePM(Cache);
93    if (PM->GetArchives(&Fetcher, &List, &Recs) == false ||
94        _error->PendingError() == true)
95       return false;
96
97    if (_error->PendingError() == true)
98       return false;
99
100    while (1)
101    {
102       bool Transient = false;
103       if (_config->FindB("APT::Get::Download",true) == false)
104       {
105          for (pkgAcquire::ItemIterator I = Fetcher.ItemsBegin(); I < Fetcher.ItemsEnd();)
106          {
107             if ((*I)->Local == true)
108             {
109                I++;
110                continue;
111             }
112
113             // Close the item and check if it was found in cache
114             (*I)->Finished();
115             if ((*I)->Complete == false)
116                Transient = true;
117
118             // Clear it out of the fetch list
119             delete *I;
120             I = Fetcher.ItemsBegin();
121          }
122       }
123
124       if (Fetcher.Run() == pkgAcquire::Failed)
125          return false;
126
127       // Print out errors
128       bool Failed = false;
129       for (pkgAcquire::ItemIterator I = Fetcher.ItemsBegin(); I != Fetcher.ItemsEnd(); I++)
130       {
131          if ((*I)->Status == pkgAcquire::Item::StatDone &&
132              (*I)->Complete == true)
133             continue;
134
135          if ((*I)->Status == pkgAcquire::Item::StatIdle)
136          {
137             Transient = true;
138             // Failed = true;
139             continue;
140          }
141
142          fprintf(stderr,("Failed to fetch %s  %s\n"),(*I)->DescURI().c_str(),
143                  (*I)->ErrorText.c_str());
144          Failed = true;
145       }
146
147       /* If we are in no download mode and missing files and there were
148          'failures' then the user must specify -m. Furthermore, there
149          is no such thing as a transient error in no-download mode! */
150       if (Transient == true &&
151           _config->FindB("APT::Get::Download",true) == false)
152       {
153          Transient = false;
154          Failed = true;
155       }
156
157       if (_config->FindB("APT::Get::Download-Only",false) == true)
158       {
159          if (Failed == true && _config->FindB("APT::Get::Fix-Missing",false) == false)
160             return _error->Error(("Some files failed to download"));
161          //c1out << _("Download complete and in download only mode") << endl;
162          return true;
163       }
164
165       if (Failed == true && _config->FindB("APT::Get::Fix-Missing",false) == false)
166       {
167          return _error->Error(("Unable to fetch some archives, maybe run apt-get update or try with --fix-missing?"));
168       }
169
170       if (Transient == true && Failed == true)
171          return _error->Error(("--fix-missing and media swapping is not currently supported"));
172
173       // Try to deal with missing package files
174       if (Failed == true && PM->FixMissing() == false)
175       {
176          cerr << ("Unable to correct missing packages.") << endl;
177          return _error->Error(("Aborting install."));
178       }
179
180       _system->UnLock();
181       int status_fd = _config->FindI("APT::Status-Fd",-1);
182       pkgPackageManager::OrderResult Res = PM->DoInstall(status_fd);
183       if (Res == pkgPackageManager::Failed || _error->PendingError() == true)
184          return false;
185       if (Res == pkgPackageManager::Completed)
186          return true;
187
188       // Reload the fetcher object and loop again for media swapping
189       Fetcher.Shutdown();
190       if (PM->GetArchives(&Fetcher,&List,&Recs) == false)
191          return false;
192
193       _system->Lock();
194    }
195 }
196
197 static void usage() {
198     fprintf(stderr, "Usage: %s [filename..]\n", prog);
199     exit(2);
200 }
201
202 static bool test_macro(const char *macro, set<string> *defines) {
203     return (*macro != '!' && defines->find(macro) != defines->end())
204             || (*macro == '!' && defines->find(macro + 1) == defines->end());
205 }
206
207 static pkgCache::PkgIterator eval_pkg(inapt_package *package, pkgCacheFile &cachef) {
208     pkgCache::PkgIterator pkg;
209
210     pkgCache *cache = cachef;
211
212     if (!pkg.end()) fatal("omg"); /* TODO */
213
214     for (std::vector<std::string>::iterator i = package->alternates.begin(); i != package->alternates.end(); i++) {
215         pkg = cache->FindPkg(*i);
216
217         /* no such package */
218         if (pkg.end())
219             continue;
220
221         /* real package */
222         if (cachef[pkg].CandidateVer)
223             break;
224
225         /* virtual package */
226         if (pkg->ProvidesList) {
227             if (!pkg.ProvidesList()->NextProvides) {
228                 pkgCache::PkgIterator tmp = pkg.ProvidesList().OwnerPkg();
229                 if (package->action == inapt_action::INSTALL) {
230                     debug("selecting %s instead of %s", tmp.Name(), pkg.Name());
231                     pkg = tmp;
232                     break;
233                 } else {
234                     debug("will not remove %s instead of virtual package %s", tmp.Name(), pkg.Name());
235                 }
236             } else {
237                 debug("%s is a virtual package", pkg.Name());
238             }
239         } else {
240             debug("%s is a virtual packages with no provides", pkg.Name());
241         }
242     }
243
244     if (pkg.end()) {
245         /* todo: report all errors at the end */
246         if (package->alternates.size() == 1) {
247             fatal("%s:%d: No such package: %s", package->filename, package->linenum, package->alternates[0].c_str());
248         } else {
249             std::vector<std::string>::iterator i = package->alternates.begin();
250             std::string message = *(i++);
251             while (i != package->alternates.end()) {
252                 message.append(", ");
253                 message.append(*(i++));
254             }
255             fatal("%s:%d: No alternative available: %s", package->filename, package->linenum, message.c_str());
256         }
257     }
258
259     return pkg;
260 }
261
262 static bool test_macros(vector<std::string> *macros, set<string> *defines) {
263     bool ok = true;
264     for (vector<std::string>::iterator j = macros->begin(); j < macros->end(); j++) {
265         if (!test_macro((*j).c_str(), defines)) {
266             ok = false;
267             break;
268         }
269     }
270     return ok;
271 }
272
273 static void eval_action(inapt_action *action, set<string> *defines, vector<inapt_package *> *final_actions) {
274     for (vector<inapt_package *>::iterator i = action->packages.begin(); i < action->packages.end(); i++) {
275         if (test_macros(&(*i)->predicates, defines))
276             final_actions->push_back(*i);
277     }
278 }
279
280 static void eval_block(inapt_block *block, set<string> *defines, vector<inapt_package *> *final_actions) {
281     if (!block)
282         return;
283
284     for (vector<inapt_action *>::iterator i = block->actions.begin(); i < block->actions.end(); i++)
285         if (test_macros(&(*i)->predicates, defines))
286             eval_action(*i, defines, final_actions);
287
288     for (vector<inapt_conditional *>::iterator i = block->children.begin(); i < block->children.end(); i++) {
289         if (test_macro((*i)->condition, defines))
290             eval_block((*i)->then_block, defines, final_actions);
291         else
292             eval_block((*i)->else_block, defines, final_actions);
293     }
294 }
295
296 static void eval_profiles(inapt_block *block, set<string> *defines) {
297     if (!block)
298         return;
299
300     for (vector<inapt_profiles *>::iterator i = block->profiles.begin(); i < block->profiles.end(); i++)
301         if (test_macros(&(*i)->predicates, defines))
302             for (vector<std::string>::iterator j = (*i)->profiles.begin(); j != (*i)->profiles.end(); j++)
303                 defines->insert(*j);
304
305     for (vector<inapt_conditional *>::iterator i = block->children.begin(); i < block->children.end(); i++) {
306         if (test_macro((*i)->condition, defines))
307             eval_profiles((*i)->then_block, defines);
308         else
309             eval_profiles((*i)->else_block, defines);
310     }
311 }
312
313 static void exec_actions(std::vector<inapt_package *> *final_actions) {
314     int marked = 0;
315
316     pkgInitConfig(*_config);
317     pkgInitSystem(*_config, _system);
318
319      _config->Set("Debug::pkgProblemResolver", true);
320 //     _config->Set("Debug::pkgAutoRemove", true);
321
322     OpTextProgress prog;
323     pkgCacheFile cachef;
324
325     if (cachef.Open(prog) == false) {
326         _error->DumpErrors();
327         exit(1);
328     }
329
330     pkgCache *cache = cachef;
331     pkgDepCache *DCache = cachef;
332
333     pkgDepCache::ActionGroup group (*cachef);
334
335     for (vector<inapt_package *>::iterator i = final_actions->begin(); i != final_actions->end(); i++)
336         (*i)->pkg = eval_pkg(*i, cachef);
337
338     for (vector<inapt_package *>::iterator i = final_actions->begin(); i < final_actions->end(); i++) {
339         pkgCache::PkgIterator k = (*i)->pkg;
340         switch ((*i)->action) {
341             case inapt_action::INSTALL:
342                 if (!k.CurrentVer() || cachef[k].Delete()) {
343                     printf("preinstall %s %s:%d\n", (*i)->pkg.Name(), (*i)->filename, (*i)->linenum);
344                     DCache->MarkInstall(k, true);
345                 }
346                 break;
347             case inapt_action::REMOVE:
348                 break;
349             default:
350                 fatal("uninitialized action");
351         }
352     }
353
354     for (vector<inapt_package *>::iterator i = final_actions->begin(); i < final_actions->end(); i++) {
355         pkgCache::PkgIterator k = (*i)->pkg;
356         switch ((*i)->action) {
357             case inapt_action::INSTALL:
358                 if ((!k.CurrentVer() && !cachef[k].Install()) || cachef[k].Delete()) {
359                     printf("install %s %s:%d\n", (*i)->pkg.Name(), (*i)->filename, (*i)->linenum);
360                     DCache->MarkInstall(k, false);
361                 }
362                 if (cachef[k].Flags & pkgCache::Flag::Auto) {
363                     marked++;
364                     debug("marking %s as manually installed", (*i)->pkg.Name());
365                     cachef->MarkAuto(k, false);
366                 }
367                 break;
368             case inapt_action::REMOVE:
369                 if ((k.CurrentVer() && !cachef[k].Delete()) || cachef[k].Install()) {
370                     printf("remove %s %s:%d\n", (*i)->pkg.Name(), (*i)->filename, (*i)->linenum);
371                     DCache->MarkDelete(k, false);
372                 }
373                 break;
374             default:
375                 fatal("uninitialized action");
376         }
377     }
378
379     fprintf(stderr, "\n");
380     fprintf(stderr, "nondownloadable packages:\n");
381     for (pkgCache::PkgIterator i = cache->PkgBegin(); !i.end(); i++) {
382        if (i.CurrentVer() && !i.CurrentVer().Downloadable()) {
383                fprintf(stderr, "%s ", i.Name());
384                fprintf(stderr, "%s\n", DCache->GetCandidateVer(i).VerStr());
385        }
386     }
387
388     fprintf(stderr, "\n");
389     fprintf(stderr, "inst %lu del %lu keep %lu broken %lu bad %lu\n",
390                     DCache->InstCount(), DCache->DelCount(), DCache->KeepCount(),
391                     DCache->BrokenCount(), DCache->BadCount());
392
393     for (pkgCache::PkgIterator i = cache->PkgBegin(); !i.end(); i++) {
394        if ((*DCache)[i].Install())
395          fprintf(stderr, "inst %s\n", i.Name());
396        if ((*DCache)[i].InstBroken())
397          fprintf(stderr, "instbroken %s\n", i.Name());
398        if ((*DCache)[i].NowBroken())
399          fprintf(stderr, "nowbroken %s\n", i.Name());
400     }
401
402     fprintf(stderr, "\n");
403
404     pkgProblemResolver fix (DCache);
405
406     for (vector<inapt_package *>::iterator i = final_actions->begin(); i < final_actions->end(); i++)
407         fix.Protect((*i)->pkg);
408     fix.Resolve();
409
410     fprintf(stderr, "\n");
411     fprintf(stderr, "inst %lu del %lu keep %lu broken %lu bad %lu\n",
412                     DCache->InstCount(), DCache->DelCount(), DCache->KeepCount(),
413                     DCache->BrokenCount(), DCache->BadCount());
414     for (pkgCache::PkgIterator i = cache->PkgBegin(); !i.end(); i++) {
415        if ((*DCache)[i].Install())
416          fprintf(stderr, "inst %s\n", i.Name());
417        if ((*DCache)[i].Delete())
418          fprintf(stderr, "del %s\n", i.Name());
419        if ((*DCache)[i].InstBroken())
420          fprintf(stderr, "instbroken %s\n", i.Name());
421        if ((*DCache)[i].NowBroken())
422          fprintf(stderr, "nowbroken %s\n", i.Name());
423     }
424
425     fprintf(stderr, "\n");
426
427     run_install(cachef);
428
429     if (marked) {
430         debug("marked %d packages, writing state file", marked);
431         cachef->writeStateFile(NULL);
432     }
433
434     for (pkgCache::PkgIterator i = cache->PkgBegin(); !i.end(); i++)
435         cachef->MarkAuto(i, true);
436
437     AwesomeRootSetFunc root (final_actions);
438
439     DCache->MarkAndSweep(root);
440     fprintf(stderr, "\n");
441     fprintf(stderr, "garbage packages:\n");
442     for (pkgCache::PkgIterator i = cache->PkgBegin(); !i.end(); i++) {
443        if (i.CurrentVer() && cachef[i].Garbage) {
444                fprintf(stderr, "%s ", i.Name());
445                fprintf(stderr, "%s\n", DCache->GetCandidateVer(i).VerStr());
446        }
447     }
448 }
449
450 static void debug_profiles(std::set<std::string> *defines) {
451     fprintf(stderr, "defines: ");
452     for (set<string>::iterator i = defines->begin(); i != defines->end(); i++)
453         fprintf(stderr, "%s ", i->c_str());
454     fprintf(stderr, "\n");
455 }
456
457 static void auto_profiles(std::set<std::string> *defines) {
458     struct utsname uts;
459     if (uname(&uts))
460         fatalpe("uname");
461     defines->insert(uts.nodename);
462 }
463
464 int main(int argc, char *argv[]) {
465     int opt;
466
467     set<string> defines;
468
469     prog = xstrdup(basename(argv[0]));
470     while ((opt = getopt_long(argc, argv, "p:", opts, NULL)) != -1) {
471         switch (opt) {
472             case 'p':
473                 defines.insert(optarg);
474                 break;
475             case '?':
476                 usage();
477                 break;
478             default:
479                 fatal("error parsing arguments");
480         }
481     }
482
483     int num_files = argc - optind;
484
485     inapt_block context;
486     vector<inapt_package *> final_actions;
487
488     while (num_files--)
489         parser(argv[optind++], &context);
490
491     auto_profiles(&defines);
492     eval_profiles(&context, &defines);
493     debug_profiles(&defines);
494     eval_block(&context, &defines, &final_actions);
495     exec_actions(&final_actions);
496 }