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