cf3e9dcd86583492e01d81283c6683307e419311
[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 char *prog = NULL;
24
25 static struct option opts[] = {
26     { "simulate", 0, NULL, 's' },
27     { "purge", 0, NULL, 'u' },
28     { NULL, 0, NULL, '\0' },
29 };
30
31 bool run_install(pkgCacheFile &cache) {
32    if (_config->FindB("Inapt::Purge", false))
33       for (pkgCache::PkgIterator i = cache->PkgBegin(); !i.end(); i++)
34          if (!i.Purge() && cache[i].Mode == pkgDepCache::ModeDelete)
35             cache->MarkDelete(i, true);
36
37    if (cache->BrokenCount())
38        fatal("broken packages during install");
39
40    if (!cache->DelCount() && !cache->InstCount() && !cache->BadCount())
41       return true;
42
43    pkgRecords Recs (cache);
44    if (_error->PendingError())
45       return false;
46
47    FileFd Lock;
48    Lock.Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
49    if (_error->PendingError())
50        return _error->Error("Unable to lock the download directory");
51
52    unsigned int width = 80;
53    AcqTextStatus status (width, 0);
54    pkgAcquire Fetcher (&status);
55
56    pkgSourceList List;
57    if (List.ReadMainList() == false)
58       return _error->Error("The list of sources could not be read");
59
60    SPtr<pkgPackageManager> PM = _system->CreatePM(cache);
61    if (PM->GetArchives(&Fetcher, &List, &Recs) == false ||
62        _error->PendingError())
63       return false;
64
65   if (Fetcher.Run() == pkgAcquire::Failed)
66      return false;
67
68   bool Failed = false;
69   for (pkgAcquire::ItemIterator i = Fetcher.ItemsBegin(); i != Fetcher.ItemsEnd(); i++) {
70      if ((*i)->Status != pkgAcquire::Item::StatDone || (*i)->Complete != true) {
71          fprintf(stderr,("Failed to fetch %s  %s\n"),(*i)->DescURI().c_str(),
72                  (*i)->ErrorText.c_str());
73          Failed = true;
74      }
75   }
76
77   if (Failed)
78      return _error->Error("Unable to fetch some archives");
79
80   _system->UnLock();
81
82   pkgPackageManager::OrderResult Res = PM->DoInstall(-1);
83   if (Res == pkgPackageManager::Completed)
84      return true;
85
86   return false;
87 }
88
89 void run_autoremove(pkgCacheFile &cache) {
90     bool purge = _config->FindB("Inapt::Purge", false);
91
92     for (pkgCache::PkgIterator i = cache->PkgBegin(); !i.end(); i++) {
93         if (cache[i].Garbage) {
94             debug("autoremove: %s", i.Name());
95             cache->MarkDelete(i, purge);
96         }
97     }
98
99     if (cache->BrokenCount())
100         fatal("automatic removal broke packages");
101 }
102
103 static void usage() {
104     fprintf(stderr, "Usage: %s [options] [filename..]\n", prog);
105     exit(2);
106 }
107
108 static bool test_profile(const char *profile, std::set<std::string> *defines) {
109     return (*profile != '!' && defines->find(profile) != defines->end())
110             || (*profile == '!' && defines->find(profile + 1) == defines->end());
111 }
112
113 static bool test_anyprofile(std::string &profile, std::set<std::string> *defines) {
114     char *s = xstrdup(profile.c_str());
115     const char *c = strtok(s, "/");
116
117     if (test_profile(c, defines)) {
118         free(s);
119         return true;
120     }
121
122     while ((c = strtok(NULL, "/")) != NULL) {
123         if (test_profile(c, defines)) {
124             free(s);
125             return true;
126         }
127     }
128
129     free(s);
130     return false;
131 }
132
133 static pkgCache::PkgIterator eval_pkg(inapt_package *package, pkgCacheFile &cache) {
134     pkgCache::PkgIterator pkg;
135
136     for (std::vector<std::string>::iterator i = package->alternates.begin(); i != package->alternates.end(); i++) {
137         pkgCache::PkgIterator tmp = cache->FindPkg(*i);
138
139         /* no such package */
140         if (tmp.end())
141             continue;
142
143         /* real package */
144         if (cache[tmp].CandidateVer) {
145             pkg = tmp;
146             break;
147         }
148
149         /* virtual package */
150         if (tmp->ProvidesList) {
151             if (!tmp.ProvidesList()->NextProvides) {
152                 pkgCache::PkgIterator provide = tmp.ProvidesList().OwnerPkg();
153                 if (package->action == inapt_action::INSTALL) {
154                     debug("selecting %s instead of %s", provide.Name(), tmp.Name());
155                     pkg = provide;
156                     break;
157                 } else {
158                     debug("will not remove %s instead of virtual package %s", provide.Name(), tmp.Name());
159                 }
160             } else {
161                 debug("%s is a virtual package", tmp.Name());
162             }
163         } else {
164             debug("%s is a virtual packages with no provides", tmp.Name());
165         }
166     }
167
168     if (pkg.end()) {
169         if (package->alternates.size() == 1) {
170             _error->Error("%s:%d: No such package: %s", package->filename, package->linenum, package->alternates[0].c_str());
171         } else {
172             std::vector<std::string>::iterator i = package->alternates.begin();
173             std::string message = *(i++);
174             while (i != package->alternates.end()) {
175                 message.append(", ").append(*(i++));
176             }
177             _error->Error("%s:%d: No alternative available: %s", package->filename, package->linenum, message.c_str());
178         }
179     }
180
181     return pkg;
182 }
183
184 static bool test_profiles(vector<std::string> *profiles, std::set<std::string> *defines) {
185     bool ok = true;
186     for (vector<std::string>::iterator j = profiles->begin(); j < profiles->end(); j++) {
187         if (!test_anyprofile(*j, defines)) {
188             ok = false;
189             break;
190         }
191     }
192     return ok;
193 }
194
195 static void eval_action(inapt_action *action, std::set<std::string> *defines, std::vector<inapt_package *> *final_actions) {
196     for (vector<inapt_package *>::iterator i = action->packages.begin(); i < action->packages.end(); i++) {
197         if (test_profiles(&(*i)->predicates, defines))
198             final_actions->push_back(*i);
199     }
200 }
201
202 static void eval_block(inapt_block *block, std::set<std::string> *defines, std::vector<inapt_package *> *final_actions) {
203     if (!block)
204         return;
205
206     for (vector<inapt_action *>::iterator i = block->actions.begin(); i < block->actions.end(); i++)
207         if (test_profiles(&(*i)->predicates, defines))
208             eval_action(*i, defines, final_actions);
209
210     for (vector<inapt_conditional *>::iterator i = block->children.begin(); i < block->children.end(); i++) {
211         if (test_profiles(&(*i)->predicates, defines))
212             eval_block((*i)->then_block, defines, final_actions);
213         else
214             eval_block((*i)->else_block, defines, final_actions);
215     }
216 }
217
218 static void eval_profiles(inapt_block *block, std::set<std::string> *defines) {
219     if (!block)
220         return;
221
222     for (vector<inapt_profiles *>::iterator i = block->profiles.begin(); i < block->profiles.end(); i++)
223         if (test_profiles(&(*i)->predicates, defines))
224             for (vector<std::string>::iterator j = (*i)->profiles.begin(); j != (*i)->profiles.end(); j++)
225                 defines->insert(*j);
226
227     for (vector<inapt_conditional *>::iterator i = block->children.begin(); i < block->children.end(); i++) {
228         if (test_profiles(&(*i)->predicates, defines))
229             eval_profiles((*i)->then_block, defines);
230         else
231             eval_profiles((*i)->else_block, defines);
232     }
233 }
234
235 static void dump_nondownloadable(pkgCacheFile &cache) {
236     for (pkgCache::PkgIterator i = cache->PkgBegin(); !i.end(); i++)
237        if (i.CurrentVer() && !i.CurrentVer().Downloadable())
238            debug("package %s version %s is not downloadable", i.Name(), i.CurrentVer().VerStr());
239 }
240
241 static void dump_actions(pkgCacheFile &cache) {
242     debug("inst %lu del %lu keep %lu broken %lu bad %lu",
243                     cache->InstCount(), cache->DelCount(), cache->KeepCount(),
244                     cache->BrokenCount(), cache->BadCount());
245     for (pkgCache::PkgIterator i = cache->PkgBegin(); !i.end(); i++) {
246        if (cache[i].Install())
247          debug("installing %s", i.Name());
248        if (cache[i].Delete())
249          debug("removing %s", i.Name());
250        if (cache[i].InstBroken())
251          debug("install broken %s", i.Name());
252        if (cache[i].NowBroken())
253          debug("now broken %s", i.Name());
254     }
255 }
256
257 static bool sanity_check(std::vector<inapt_package *> *final_actions, pkgCacheFile &cache) {
258     bool okay = true;
259     std::map<std::string, inapt_package *> packages;
260
261     for (vector<inapt_package *>::iterator i = final_actions->begin(); i != final_actions->end(); i++) {
262         if (packages.find((*i)->pkg.Name()) != packages.end()) {
263             inapt_package *first = packages[(*i)->pkg.Name()];
264             inapt_package *current = *i;
265             _error->Error("Multiple directives for package %s at %s:%d and %s:%d",
266                     (*i)->pkg.Name(), first->filename, first->linenum, current->filename, current->linenum);
267             debug("foo %s", (*i)->pkg.Name());
268             okay = false;
269             continue;
270         }
271         packages[(*i)->pkg.Name()] = *i;
272     }
273
274     for (pkgCache::PkgIterator i = cache->PkgBegin(); !i.end(); i++) {
275         if (cache[i].Delete() && (i->Flags & pkgCache::Flag::Essential || i->Flags & pkgCache::Flag::Important)) {
276             _error->Error("Removing essential package %s", i.Name());
277             okay = false;
278         }
279     }
280
281     return okay;
282 }
283
284 static void show_breakage(pkgCacheFile &cache) {
285     std::string broken;
286     for (pkgCache::PkgIterator i = cache->PkgBegin(); !i.end(); i++)
287         if (cache[i].NowBroken() || cache[i].InstBroken())
288             broken.append(" ").append(i.Name());
289
290     _error->Error("Broken packages:%s", broken.c_str());
291 }
292
293 static void exec_actions(std::vector<inapt_package *> *final_actions) {
294     int marked = 0;
295     bool purge = _config->FindB("Inapt::Purge", false);
296
297     pkgInitConfig(*_config);
298     pkgInitSystem(*_config, _system);
299
300     OpTextProgress prog;
301     pkgCacheFile cache;
302
303     if (cache.Open(prog) == false)
304         return;
305
306     pkgDepCache::ActionGroup group (cache);
307
308     for (vector<inapt_package *>::iterator i = final_actions->begin(); i != final_actions->end(); i++)
309         (*i)->pkg = eval_pkg(*i, cache);
310
311     if (_error->PendingError())
312         return;
313
314     // preliminary loop (auto-installs, includes recommends - could do this manually)
315     for (vector<inapt_package *>::iterator i = final_actions->begin(); i < final_actions->end(); i++) {
316         pkgCache::PkgIterator k = (*i)->pkg;
317         switch ((*i)->action) {
318             case inapt_action::INSTALL:
319                 if (!k.CurrentVer() || cache[k].Delete()) {
320                     debug("install %s %s:%d", (*i)->pkg.Name(), (*i)->filename, (*i)->linenum);
321                     cache->MarkInstall(k, true);
322                 }
323                 break;
324             case inapt_action::REMOVE:
325                 break;
326             default:
327                 fatal("uninitialized action");
328         }
329     }
330
331     // secondary loop (removes package and reinstalls auto-removed packages)
332     for (vector<inapt_package *>::iterator i = final_actions->begin(); i < final_actions->end(); i++) {
333         pkgCache::PkgIterator k = (*i)->pkg;
334         switch ((*i)->action) {
335             case inapt_action::INSTALL:
336                 if ((!k.CurrentVer() && !cache[k].Install()) || cache[k].Delete()) {
337                     debug("force install %s %s:%d", (*i)->pkg.Name(), (*i)->filename, (*i)->linenum);
338                     cache->MarkInstall(k, false);
339                 }
340                 if (cache[k].Flags & pkgCache::Flag::Auto) {
341                     debug("marking %s as manually installed", (*i)->pkg.Name());
342                     cache->MarkAuto(k, false);
343                     marked++;
344                 }
345                 break;
346             case inapt_action::REMOVE:
347                 if ((k.CurrentVer() && !cache[k].Delete()) || cache[k].Install())
348                     debug("remove %s %s:%d", (*i)->pkg.Name(), (*i)->filename, (*i)->linenum);
349
350                 /* always mark so purge works */
351                 cache->MarkDelete(k, purge);
352                 break;
353             default:
354                 fatal("uninitialized action");
355         }
356     }
357
358     if (_error->PendingError())
359         return;
360
361     dump_nondownloadable(cache);
362     dump_actions(cache);
363
364     if (cache->BrokenCount()) {
365         pkgProblemResolver fix (cache);
366         for (vector<inapt_package *>::iterator i = final_actions->begin(); i < final_actions->end(); i++)
367             fix.Protect((*i)->pkg);
368         fix.Resolve();
369
370         if (cache->BrokenCount()) {
371             show_breakage(cache);
372             return;
373         }
374     }
375
376     cache->MarkAndSweep();
377     run_autoremove(cache);
378
379     if (!sanity_check(final_actions, cache))
380         return;
381
382     if (_config->FindB("Inapt::Simulate", false)) {
383         pkgSimulate PM (cache);
384         PM.DoInstall(-1);
385         return;
386     }
387
388     run_install(cache);
389     if (_error->PendingError())
390         return;
391
392     if (marked) {
393         if (_config->FindB("Inapt::Simulate", false)) {
394             debug("marked %d packages", marked);
395         } else {
396             debug("marked %d packages, writing state file", marked);
397             cache->writeStateFile(NULL);
398         }
399     }
400 }
401
402 static void debug_profiles(std::set<std::string> *defines) {
403     std::string profiles = "profiles:";
404
405     for (std::set<std::string>::iterator i = defines->begin(); i != defines->end(); i++) {
406         profiles.append(" ");
407         profiles.append(*i);
408     }
409
410     debug("%s", profiles.c_str());
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 static void set_option(char *opt) {
421     char *eq = strchr(opt, '=');
422     if (!eq)
423         fatal("invalid syntax for '%s': must be <option>=<value>", opt);
424
425     std::string option (opt, eq - opt);
426     std::string value (eq + 1);
427
428     _config->Set(option, value);
429 }
430
431 int main(int argc, char *argv[]) {
432     int opt;
433
434     std::set<std::string> defines;
435
436     prog = xstrdup(basename(argv[0]));
437     while ((opt = getopt_long(argc, argv, "p:o:sd", opts, NULL)) != -1) {
438         switch (opt) {
439             case 'p':
440                 defines.insert(optarg);
441                 break;
442             case '?':
443                 usage();
444                 break;
445             case 's':
446                 _config->Set("Inapt::Simulate", true);
447                 break;
448             case 'u':
449                 _config->Set("Inapt::Purge", true);
450                 break;
451             case 'd':
452                 debug_level++;
453                 break;
454             case 'o':
455                 set_option(optarg);
456                 break;
457             default:
458                 fatal("error parsing arguments");
459         }
460     }
461
462     int num_files = argc - optind;
463
464     inapt_block context;
465     std::vector<inapt_package *> final_actions;
466
467     if (!num_files)
468         parser(NULL, &context);
469
470     while (num_files--)
471         parser(argv[optind++], &context);
472
473     auto_profiles(&defines);
474     eval_profiles(&context, &defines);
475     debug_profiles(&defines);
476     eval_block(&context, &defines, &final_actions);
477     exec_actions(&final_actions);
478
479     if (_error->PendingError()) {
480         _error->DumpErrors();
481         exit(1);
482     }
483 }