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