Finish renaming macros 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 "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 pkgCache::PkgIterator eval_pkg(inapt_package *package, pkgCacheFile &cache) {
114     pkgCache::PkgIterator pkg;
115
116     for (std::vector<std::string>::iterator i = package->alternates.begin(); i != package->alternates.end(); i++) {
117         pkgCache::PkgIterator tmp = cache->FindPkg(*i);
118
119         /* no such package */
120         if (tmp.end())
121             continue;
122
123         /* real package */
124         if (cache[tmp].CandidateVer) {
125             pkg = tmp;
126             break;
127         }
128
129         /* virtual package */
130         if (tmp->ProvidesList) {
131             if (!tmp.ProvidesList()->NextProvides) {
132                 pkgCache::PkgIterator provide = tmp.ProvidesList().OwnerPkg();
133                 if (package->action == inapt_action::INSTALL) {
134                     debug("selecting %s instead of %s", provide.Name(), tmp.Name());
135                     pkg = provide;
136                     break;
137                 } else {
138                     debug("will not remove %s instead of virtual package %s", provide.Name(), tmp.Name());
139                 }
140             } else {
141                 debug("%s is a virtual package", tmp.Name());
142             }
143         } else {
144             debug("%s is a virtual packages with no provides", tmp.Name());
145         }
146     }
147
148     if (pkg.end()) {
149         if (package->alternates.size() == 1) {
150             _error->Error("%s:%d: No such package: %s", package->filename, package->linenum, package->alternates[0].c_str());
151         } else {
152             std::vector<std::string>::iterator i = package->alternates.begin();
153             std::string message = *(i++);
154             while (i != package->alternates.end()) {
155                 message.append(", ");
156                 message.append(*(i++));
157             }
158             _error->Error("%s:%d: No alternative available: %s", package->filename, package->linenum, message.c_str());
159         }
160     }
161
162     return pkg;
163 }
164
165 static bool test_profiles(vector<std::string> *profiles, std::set<std::string> *defines) {
166     bool ok = true;
167     for (vector<std::string>::iterator j = profiles->begin(); j < profiles->end(); j++) {
168         if (!test_profile((*j).c_str(), defines)) {
169             ok = false;
170             break;
171         }
172     }
173     return ok;
174 }
175
176 static void eval_action(inapt_action *action, std::set<std::string> *defines, std::vector<inapt_package *> *final_actions) {
177     for (vector<inapt_package *>::iterator i = action->packages.begin(); i < action->packages.end(); i++) {
178         if (test_profiles(&(*i)->predicates, defines))
179             final_actions->push_back(*i);
180     }
181 }
182
183 static void eval_block(inapt_block *block, std::set<std::string> *defines, std::vector<inapt_package *> *final_actions) {
184     if (!block)
185         return;
186
187     for (vector<inapt_action *>::iterator i = block->actions.begin(); i < block->actions.end(); i++)
188         if (test_profiles(&(*i)->predicates, defines))
189             eval_action(*i, defines, final_actions);
190
191     for (vector<inapt_conditional *>::iterator i = block->children.begin(); i < block->children.end(); i++) {
192         if (test_profiles(&(*i)->predicates, defines))
193             eval_block((*i)->then_block, defines, final_actions);
194         else
195             eval_block((*i)->else_block, defines, final_actions);
196     }
197 }
198
199 static void eval_profiles(inapt_block *block, std::set<std::string> *defines) {
200     if (!block)
201         return;
202
203     for (vector<inapt_profiles *>::iterator i = block->profiles.begin(); i < block->profiles.end(); i++)
204         if (test_profiles(&(*i)->predicates, defines))
205             for (vector<std::string>::iterator j = (*i)->profiles.begin(); j != (*i)->profiles.end(); j++)
206                 defines->insert(*j);
207
208     for (vector<inapt_conditional *>::iterator i = block->children.begin(); i < block->children.end(); i++) {
209         if (test_profiles(&(*i)->predicates, defines))
210             eval_profiles((*i)->then_block, defines);
211         else
212             eval_profiles((*i)->else_block, defines);
213     }
214 }
215
216 static void dump_nondownloadable(pkgCacheFile &cache) {
217     for (pkgCache::PkgIterator i = cache->PkgBegin(); !i.end(); i++)
218        if (i.CurrentVer() && !i.CurrentVer().Downloadable())
219            debug("package %s version %s is not downloadable", i.Name(), i.CurrentVer().VerStr());
220 }
221
222 static void dump_actions(pkgCacheFile &cache) {
223     debug("inst %lu del %lu keep %lu broken %lu bad %lu",
224                     cache->InstCount(), cache->DelCount(), cache->KeepCount(),
225                     cache->BrokenCount(), cache->BadCount());
226     for (pkgCache::PkgIterator i = cache->PkgBegin(); !i.end(); i++) {
227        if (cache[i].Install())
228          debug("installing %s", i.Name());
229        if (cache[i].Delete())
230          debug("removing %s", i.Name());
231        if (cache[i].InstBroken())
232          debug("install broken %s", i.Name());
233        if (cache[i].NowBroken())
234          debug("now broken %s", i.Name());
235     }
236 }
237
238 static bool sanity_check(std::vector<inapt_package *> *final_actions, pkgCacheFile &cache) {
239     bool okay = true;
240     std::map<std::string, inapt_package *> packages;
241
242     for (vector<inapt_package *>::iterator i = final_actions->begin(); i != final_actions->end(); i++) {
243         if (packages.find((*i)->pkg.Name()) != packages.end()) {
244             inapt_package *first = packages[(*i)->pkg.Name()];
245             inapt_package *current = *i;
246             _error->Error("Multiple directives for package %s at %s:%d and %s:%d",
247                     (*i)->pkg.Name(), first->filename, first->linenum, current->filename, current->linenum);
248             debug("foo %s", (*i)->pkg.Name());
249             okay = false;
250             continue;
251         }
252         packages[(*i)->pkg.Name()] = *i;
253     }
254
255     for (pkgCache::PkgIterator i = cache->PkgBegin(); !i.end(); i++) {
256         if (cache[i].Delete() && (i->Flags & pkgCache::Flag::Essential || i->Flags & pkgCache::Flag::Important)) {
257             _error->Error("Removing essential package %s", i.Name());
258             okay = false;
259         }
260     }
261
262     return okay;
263 }
264
265 static void show_breakage(pkgCacheFile &cache) {
266     fprintf(stderr, "fatal: Unable to solve dependencies\n");
267     fprintf(stderr, "The following packages are broken:");
268     for (pkgCache::PkgIterator i = cache->PkgBegin(); !i.end(); i++)
269         if (cache[i].NowBroken() || cache[i].InstBroken())
270             fprintf(stderr, " %s", i.Name());
271     fprintf(stderr, "\n");
272     exit(1);
273 }
274
275 static void exec_actions(std::vector<inapt_package *> *final_actions) {
276     int marked = 0;
277     bool purge = _config->FindB("Inapt::Purge", false);
278
279     pkgInitConfig(*_config);
280     pkgInitSystem(*_config, _system);
281
282     OpTextProgress prog;
283     pkgCacheFile cache;
284
285     if (cache.Open(prog) == false)
286         return;
287
288     pkgDepCache::ActionGroup group (cache);
289
290     for (vector<inapt_package *>::iterator i = final_actions->begin(); i != final_actions->end(); i++)
291         (*i)->pkg = eval_pkg(*i, cache);
292
293     if (_error->PendingError())
294         return;
295
296     // preliminary loop (auto-installs, includes recommends - could do this manually)
297     for (vector<inapt_package *>::iterator i = final_actions->begin(); i < final_actions->end(); i++) {
298         pkgCache::PkgIterator k = (*i)->pkg;
299         switch ((*i)->action) {
300             case inapt_action::INSTALL:
301                 if (!k.CurrentVer() || cache[k].Delete()) {
302                     debug("install %s %s:%d", (*i)->pkg.Name(), (*i)->filename, (*i)->linenum);
303                     cache->MarkInstall(k, true);
304                 }
305                 break;
306             case inapt_action::REMOVE:
307                 break;
308             default:
309                 fatal("uninitialized action");
310         }
311     }
312
313     // secondary loop (removes package and reinstalls auto-removed packages)
314     for (vector<inapt_package *>::iterator i = final_actions->begin(); i < final_actions->end(); i++) {
315         pkgCache::PkgIterator k = (*i)->pkg;
316         switch ((*i)->action) {
317             case inapt_action::INSTALL:
318                 if ((!k.CurrentVer() && !cache[k].Install()) || cache[k].Delete()) {
319                     debug("force install %s %s:%d", (*i)->pkg.Name(), (*i)->filename, (*i)->linenum);
320                     cache->MarkInstall(k, false);
321                 }
322                 if (cache[k].Flags & pkgCache::Flag::Auto) {
323                     debug("marking %s as manually installed", (*i)->pkg.Name());
324                     cache->MarkAuto(k, false);
325                     marked++;
326                 }
327                 break;
328             case inapt_action::REMOVE:
329                 if ((k.CurrentVer() && !cache[k].Delete()) || cache[k].Install())
330                     debug("remove %s %s:%d", (*i)->pkg.Name(), (*i)->filename, (*i)->linenum);
331
332                 /* always mark so purge works */
333                 cache->MarkDelete(k, purge);
334                 break;
335             default:
336                 fatal("uninitialized action");
337         }
338     }
339
340     if (_error->PendingError())
341         return;
342
343     dump_nondownloadable(cache);
344     dump_actions(cache);
345
346     if (cache->BrokenCount()) {
347         pkgProblemResolver fix (cache);
348         for (vector<inapt_package *>::iterator i = final_actions->begin(); i < final_actions->end(); i++)
349             fix.Protect((*i)->pkg);
350         fix.Resolve();
351
352         if (cache->BrokenCount())
353             show_breakage(cache);
354     }
355
356     cache->MarkAndSweep();
357     run_autoremove(cache);
358
359     if (!sanity_check(final_actions, cache))
360         return;
361
362     if (_config->FindB("Inapt::Simulate", false)) {
363         pkgSimulate PM (cache);
364         PM.DoInstall(-1);
365         return;
366     }
367
368     run_install(cache);
369     if (_error->PendingError())
370         return;
371
372     if (marked) {
373         if (_config->FindB("Inapt::Simulate", false)) {
374             debug("marked %d packages", marked);
375         } else {
376             debug("marked %d packages, writing state file", marked);
377             cache->writeStateFile(NULL);
378         }
379     }
380 }
381
382 static void debug_profiles(std::set<std::string> *defines) {
383     std::string profiles = "profiles:";
384
385     for (std::set<std::string>::iterator i = defines->begin(); i != defines->end(); i++) {
386         profiles.append(" ");
387         profiles.append(*i);
388     }
389
390     debug("%s", profiles.c_str());
391 }
392
393 static void auto_profiles(std::set<std::string> *defines) {
394     struct utsname uts;
395     if (uname(&uts))
396         fatalpe("uname");
397     defines->insert(uts.nodename);
398 }
399
400 static void set_option(char *opt) {
401     char *eq = strchr(opt, '=');
402     if (!eq)
403         fatal("invalid syntax for '%s': must be <option>=<value>", opt);
404
405     std::string option (opt, eq - opt);
406     std::string value (eq + 1);
407
408     _config->Set(option, value);
409 }
410
411 int main(int argc, char *argv[]) {
412     int opt;
413
414     std::set<std::string> defines;
415
416     prog = xstrdup(basename(argv[0]));
417     while ((opt = getopt_long(argc, argv, "p:o:sd", opts, NULL)) != -1) {
418         switch (opt) {
419             case 'p':
420                 defines.insert(optarg);
421                 break;
422             case '?':
423                 usage();
424                 break;
425             case 's':
426                 _config->Set("Inapt::Simulate", true);
427                 break;
428             case 'u':
429                 _config->Set("Inapt::Purge", true);
430                 break;
431             case 'd':
432                 debug_enabled = true;
433                 break;
434             case 'o':
435                 set_option(optarg);
436                 break;
437             default:
438                 fatal("error parsing arguments");
439         }
440     }
441
442     int num_files = argc - optind;
443
444     inapt_block context;
445     std::vector<inapt_package *> final_actions;
446
447     if (!num_files)
448         parser(NULL, &context);
449
450     while (num_files--)
451         parser(argv[optind++], &context);
452
453     auto_profiles(&defines);
454     eval_profiles(&context, &defines);
455     debug_profiles(&defines);
456     eval_block(&context, &defines, &final_actions);
457     exec_actions(&final_actions);
458
459     if (_error->PendingError()) {
460         _error->DumpErrors();
461         exit(1);
462     }
463 }