- fetch primary location from repomd.xml
[platform/upstream/libsolv.git] / examples / solv.c
1 /*
2  * Copyright (c) 2009, Novell Inc.
3  *
4  * This program is licensed under the BSD license, read LICENSE.BSD
5  * for further information
6  */
7
8 /* solv, a little software installer demoing the sat solver library */
9
10 /* things missing:
11  * - repository data caching
12  * - signature verification
13  */
14
15 #define _GNU_SOURCE
16
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <dirent.h>
20 #include <unistd.h>
21 #include <zlib.h>
22 #include <fcntl.h>
23 #include <sys/utsname.h>
24 #include <sys/types.h>
25 #include <sys/wait.h>
26
27 #include "pool.h"
28 #include "poolarch.h"
29 #include "repo.h"
30 #include "util.h"
31 #include "solver.h"
32 #include "solverdebug.h"
33
34 #include "repo_rpmdb.h"
35 #include "repo_rpmmd.h"
36 #include "repo_susetags.h"
37 #include "repo_repomdxml.h"
38 #include "pool_fileconflicts.h"
39
40
41 struct repoinfo {
42   Repo *repo;
43
44   char *alias;
45   char *name;
46   int enabled;
47   int autorefresh;
48   char *baseurl;
49   char *path;
50   int type;
51   int priority;
52   int keeppackages;
53 };
54
55 #define TYPE_UNKNOWN 0
56 #define TYPE_SUSETAGS 1
57 #define TYPE_RPMMD 2
58
59 struct repoinfo *
60 read_repoinfos(Pool *pool, const char *reposdir, int *nrepoinfosp)
61 {
62   char buf[4096];
63   char buf2[4096], *kp, *vp, *kpe;
64   DIR *dir;
65   FILE *fp;
66   struct dirent *ent;
67   int l, rdlen;
68   struct repoinfo *repoinfos = 0, *cinfo;
69   int nrepoinfos = 0;
70
71   rdlen = strlen(reposdir);
72   dir = opendir(reposdir);
73   if (!dir)
74     {
75       *nrepoinfosp = 0;
76       return 0;
77     }
78   while ((ent = readdir(dir)) != 0)
79     {
80       l = strlen(ent->d_name);
81       if (l < 6 || rdlen + 2 + l >= sizeof(buf) || strcmp(ent->d_name + l - 5, ".repo") != 0)
82         continue;
83       snprintf(buf, sizeof(buf), "%s/%s", reposdir, ent->d_name);
84       if ((fp = fopen(buf, "r")) == 0)
85         {
86           perror(buf);
87           continue;
88         }
89       cinfo = 0;
90       while(fgets(buf2, sizeof(buf2), fp))
91         {
92           l = strlen(buf2);
93           if (l == 0)
94             continue;
95           while (buf2[l - 1] == '\n' || buf2[l - 1] == ' ' || buf2[l - 1] == '\t')
96             buf2[--l] = 0;
97           kp = buf2;
98           while (*kp == ' ' || *kp == '\t')
99             kp++;
100           if (!*kp || *kp == '#')
101             continue;
102           if (!cinfo)
103             {
104               if (*kp != '[')
105                 continue;
106               vp = strrchr(kp, ']');
107               if (!vp)
108                 continue;
109               *vp = 0;
110               repoinfos = sat_extend(repoinfos, nrepoinfos, 1, sizeof(*repoinfos), 15);
111               cinfo = repoinfos + nrepoinfos++;
112               memset(cinfo, 0, sizeof(*cinfo));
113               cinfo->alias = strdup(kp + 1);
114               continue;
115             }
116           vp = strchr(kp, '=');
117           if (!vp)
118             continue;
119           for (kpe = vp - 1; kpe >= kp; kpe--)
120             if (*kpe != ' ' && *kpe != '\t')
121               break;
122           if (kpe == kp)
123             continue;
124           vp++;
125           while (*vp == ' ' || *vp == '\t')
126             vp++;
127           kpe[1] = 0;
128           if (!strcmp(kp, "name"))
129             cinfo->name = strdup(vp);
130           else if (!strcmp(kp, "enabled"))
131             cinfo->enabled = *vp == '0' ? 0 : 1;
132           else if (!strcmp(kp, "autorefresh"))
133             cinfo->autorefresh = *vp == '0' ? 0 : 1;
134           else if (!strcmp(kp, "baseurl"))
135             cinfo->baseurl = strdup(vp);
136           else if (!strcmp(kp, "path"))
137             cinfo->path = strdup(vp);
138           else if (!strcmp(kp, "type"))
139             {
140               if (!strcmp(vp, "yast2"))
141                 cinfo->type = TYPE_SUSETAGS;
142               else if (!strcmp(vp, "rpm-md"))
143                 cinfo->type = TYPE_RPMMD;
144               else
145                 cinfo->type = TYPE_UNKNOWN;
146             }
147           else if (!strcmp(kp, "priority"))
148             cinfo->priority = atoi(vp);
149           else if (!strcmp(kp, "keeppackages"))
150             cinfo->keeppackages = *vp == '0' ? 0 : 1;
151         }
152       fclose(fp);
153       if (cinfo->type == TYPE_SUSETAGS && cinfo->baseurl)
154         {
155           char *old = cinfo->baseurl;
156           cinfo->baseurl = sat_malloc(5 + strlen(old) + 1);
157           sprintf(cinfo->baseurl, "%s/suse", old);
158           free(old);
159         }
160       cinfo = 0;
161     }
162   closedir(dir);
163   *nrepoinfosp = nrepoinfos;
164   return repoinfos;
165 }
166
167 static ssize_t
168 cookie_gzread(void *cookie, char *buf, size_t nbytes)
169 {
170   return gzread((gzFile *)cookie, buf, nbytes);
171 }
172
173 static int
174 cookie_gzclose(void *cookie)
175 {
176   return gzclose((gzFile *)cookie);
177 }
178
179 FILE *
180 myfopen(const char *fn)
181 {
182   cookie_io_functions_t cio;
183   char *suf;
184   gzFile *gzf;
185
186   if (!fn)
187     return 0;
188   suf = strrchr(fn, '.');
189   if (!suf || strcmp(suf, ".gz") != 0)
190     return fopen(fn, "r");
191   gzf = gzopen(fn, "r");
192   if (!gzf)
193     return 0;
194   memset(&cio, 0, sizeof(cio));
195   cio.read = cookie_gzread;
196   cio.close = cookie_gzclose;
197   return  fopencookie(gzf, "r", cio);
198 }
199
200 FILE *
201 curlfopen(const char *baseurl, const char *file, int uncompress)
202 {
203   pid_t pid;
204   int fd, l;
205   int status;
206   char tmpl[100];
207   char url[4096];
208
209   l = strlen(baseurl);
210   if (l && baseurl[l - 1] == '/')
211     snprintf(url, sizeof(url), "%s%s", baseurl, file);
212   else
213     snprintf(url, sizeof(url), "%s/%s", baseurl, file);
214   strcpy(tmpl, "/var/tmp/solvXXXXXX");
215   fd = mkstemp(tmpl);
216   if (fd < 0)
217     {
218       perror("mkstemp");
219       exit(1);
220     }
221   unlink(tmpl);
222   if ((pid = fork()) == (pid_t)-1)
223     {
224       perror("fork");
225       exit(1);
226     }
227   if (pid == 0)
228     {
229       if (fd != 1)
230         {
231           dup2(fd, 1);
232           close(fd);
233         }
234       execlp("curl", "curl", "-s", "-L", url, (char *)0);
235       perror("curl");
236       _exit(0);
237     }
238   while (waitpid(pid, &status, 0) != pid)
239     ;
240   lseek(fd, 0, SEEK_SET);
241   if (uncompress)
242     {
243       cookie_io_functions_t cio;
244       gzFile *gzf;
245
246       sprintf(tmpl, "/dev/fd/%d", fd);
247       gzf = gzopen(tmpl, "r");
248       close(fd);
249       if (!gzf)
250         return 0;
251       memset(&cio, 0, sizeof(cio));
252       cio.read = cookie_gzread;
253       cio.close = cookie_gzclose;
254       return fopencookie(gzf, "r", cio);
255     }
256   fcntl(fd, F_SETFD, FD_CLOEXEC);
257   return fdopen(fd, "r");
258 }
259
260 void
261 setarch(Pool *pool)
262 {
263   struct utsname un;
264   if (uname(&un))
265     {
266       perror("uname");
267       exit(1);
268     }
269   pool_setarch(pool, un.machine);
270 }
271
272
273 void
274 read_repos(Pool *pool, struct repoinfo *repoinfos, int nrepoinfos)
275 {
276   Repo *repo;
277   struct repoinfo *cinfo;
278   int i;
279   FILE *fp;
280   Dataiterator di;
281   const char *primaryfile;
282
283   printf("reading rpm database\n");
284   repo = repo_create(pool, "@System");
285   repo_add_rpmdb(repo, 0, 0, 0);
286   pool_set_installed(pool, repo);
287   for (i = 0; i < nrepoinfos; i++)
288     {
289       cinfo = repoinfos + i;
290       if (!cinfo->enabled)
291         continue;
292       switch (cinfo->type)
293         {
294         case TYPE_RPMMD:
295           printf("reading rpmmd repo '%s'\n", cinfo->alias);
296           if ((fp = curlfopen(cinfo->baseurl, "repodata/repomd.xml", 0)) == 0)
297             break;
298           repo = repo_create(pool, cinfo->alias);
299           cinfo->repo = repo;
300           repo->appdata = cinfo;
301           repo_add_repomdxml(repo, fp, 0);
302           fclose(fp);
303           primaryfile = 0;
304           dataiterator_init(&di, pool, repo, SOLVID_META, REPOSITORY_REPOMD_TYPE, "primary", SEARCH_STRING);
305           dataiterator_prepend_keyname(&di, REPOSITORY_REPOMD);
306           if (dataiterator_step(&di))
307             {
308               dataiterator_setpos_parent(&di);
309               primaryfile = pool_lookup_str(pool, SOLVID_POS, REPOSITORY_REPOMD_LOCATION);
310             }
311           dataiterator_free(&di);
312           if (!primaryfile)
313             primaryfile = "repodata/primary.xml.gz";
314           if ((fp = curlfopen(cinfo->baseurl, primaryfile, 1)) == 0)
315             continue;
316           repo_add_rpmmd(repo, fp, 0, 0);
317           fclose(fp);
318           break;
319         case TYPE_SUSETAGS:
320           printf("reading susetags repo '%s'\n", cinfo->alias);
321           if ((fp = curlfopen(cinfo->baseurl, "setup/descr/packages.gz", 1)) == 0)
322             break;
323           repo = repo_create(pool, cinfo->alias);
324           cinfo->repo = repo;
325           repo->appdata = cinfo;
326           repo_add_susetags(repo, fp, 0, 0, 0);
327           fclose(fp);
328           break;
329         default:
330           printf("skipping unknown repo '%s'\n", cinfo->alias);
331           break;
332         }
333     }
334 }
335
336 void
337 mkselect(Pool *pool, const char *arg, int flags, Queue *out)
338 {
339   Id id, p, pp;
340   Id type = 0;
341   const char *r, *r2;
342
343   id = str2id(pool, arg, 0);
344   if (id)
345     {
346       FOR_PROVIDES(p, pp, id)
347         {
348           Solvable *s = pool_id2solvable(pool, p);
349           if (s->name == id)
350             {
351               type = SOLVER_SOLVABLE_NAME;
352               break;
353             }
354           type = SOLVER_SOLVABLE_PROVIDES;
355         }
356     }
357   if (!type)
358     {
359       /* did not find a solvable, see if it's a relation */
360       if ((r = strpbrk(arg, "<=>")) != 0)
361         {
362           Id rid, rname, revr;
363           int rflags = 0;
364           for (r2 = r; r2 > arg && (r2[-1] == ' ' || r2[-1] == '\t'); )
365             r2--;
366           rname = r2 > arg ? strn2id(pool, arg, r2 - arg, 1) : 0;
367           for (; *r; r++)
368             {
369               if (*r == '<')
370                 rflags |= REL_LT;
371               else if (*r == '=')
372                 rflags |= REL_EQ;
373               else if (*r == '>')
374                 rflags |= REL_GT;
375               else
376                 break;
377             }
378           while (*r == ' ' || *r == '\t')
379             r++;
380           revr = *r ? str2id(pool, r, 1) : 0;
381           rid = rname && revr ? rel2id(pool, rname, revr, rflags, 1) : 0;
382           if (rid)
383             {
384               FOR_PROVIDES(p, pp, rid)
385                 {
386                   Solvable *s = pool_id2solvable(pool, p);
387                   if (pool_match_nevr(pool, s, rid))
388                     {
389                       type = SOLVER_SOLVABLE_NAME;
390                       break;
391                     }
392                   type = SOLVER_SOLVABLE_PROVIDES;
393                 }
394             }
395           if (type)
396             id = rid;
397         }
398     }
399   if (type)
400     {
401       queue_push(out, type);
402       queue_push(out, id);
403     }
404 }
405
406 int
407 yesno(const char *str)
408 {
409   char inbuf[128], *ip;
410
411   for (;;)
412     {
413       printf("%s", str);
414       fflush(stdout);
415       *inbuf = 0;
416       if (!(ip = fgets(inbuf, sizeof(inbuf), stdin)))
417         {
418           printf("Abort.\n");
419           exit(1);
420         }
421       while (*ip == ' ' || *ip == '\t')
422         ip++;
423       if (*ip == 'q')
424         {
425           printf("Abort.\n");
426           exit(1);
427         }
428       if (*ip == 'y' || *ip == 'n')
429         return *ip == 'y' ? 1 : 0;
430     }
431 }
432
433 struct fcstate {
434   FILE **newpkgsfps;
435   Queue *checkq;
436   int newpkgscnt;
437   void *rpmdbstate;
438 };
439
440 static void *
441 fc_cb(Pool *pool, Id p, void *cbdata)
442 {
443   struct fcstate *fcstate = cbdata;
444   Solvable *s;
445   Id rpmdbid;
446   int i;
447   FILE *fp;
448
449   if (!p)
450     {
451       rpm_byrpmdbid(0, 0, &fcstate->rpmdbstate);
452       return 0;
453     }
454   s = pool_id2solvable(pool, p);
455   if (pool->installed && s->repo == pool->installed)
456     {
457       if (!s->repo->rpmdbid)
458         return 0;
459       rpmdbid = s->repo->rpmdbid[p - s->repo->start];
460       if (!rpmdbid)
461         return 0;
462        return rpm_byrpmdbid(rpmdbid, 0, &fcstate->rpmdbstate);
463     }
464   for (i = 0; i < fcstate->newpkgscnt; i++)
465     if (fcstate->checkq->elements[i] == p)
466       break;
467   if (i == fcstate->newpkgscnt)
468     return 0;
469   fp = fcstate->newpkgsfps[i];
470   rewind(fp);
471   return rpm_byfp(fp, solvable2str(pool, s), &fcstate->rpmdbstate);
472 }
473
474 void
475 runrpm(const char *arg, const char *name, int dupfd3)
476 {
477   pid_t pid;
478   int status;
479
480   if ((pid = fork()) == (pid_t)-1)
481     {
482       perror("fork");
483       exit(1);
484     }
485   if (pid == 0)
486     {
487       if (dupfd3 != -1 && dupfd3 != 3)
488         {
489           dup2(dupfd3, 3);
490           close(dupfd3);
491         }
492       if (dupfd3 != -1)
493         fcntl(3, F_SETFD, 0);   /* clear CLOEXEC */
494       if (strcmp(arg, "-e") == 0)
495         execlp("rpm", "rpm", arg, "--nodeps", "--nodigest", "--nosignature", name, (char *)0);
496       else
497         execlp("rpm", "rpm", arg, "--force", "--nodeps", "--nodigest", "--nosignature", name, (char *)0);
498       perror("rpm");
499       _exit(0);
500     }
501   while (waitpid(pid, &status, 0) != pid)
502     ;
503   if (status)
504     {
505       printf("rpm failed\n");
506       exit(1);
507     }
508 }
509
510 int
511 main(int argc, char **argv)
512 {
513   Pool *pool;
514   Id p, pp;
515   struct repoinfo *repoinfos;
516   int nrepoinfos = 0;
517   int i, mode, newpkgs;
518   Queue job, checkq;
519   Solver *solv = 0;
520   Transaction *trans;
521   char inbuf[128], *ip;
522   int updateall = 0;
523   FILE **newpkgsfps;
524   struct fcstate fcstate;
525
526   if (!strcmp(argv[1], "install") || !strcmp(argv[1], "in"))
527     mode = SOLVER_INSTALL;
528   else if (!strcmp(argv[1], "erase") || !strcmp(argv[1], "rm"))
529     mode = SOLVER_ERASE;
530   else if (!strcmp(argv[1], "show"))
531     mode = 0;
532   else if (!strcmp(argv[1], "update") || !strcmp(argv[1], "up"))
533     mode = SOLVER_UPDATE;
534   else
535     {
536       fprintf(stderr, "Usage: solv install|erase|update|show <select>\n");
537       exit(1);
538     }
539
540   pool = pool_create();
541   // pool_setdebuglevel(pool, 2);
542   setarch(pool);
543   repoinfos = read_repoinfos(pool, "/etc/zypp/repos.d", &nrepoinfos);
544   read_repos(pool, repoinfos, nrepoinfos);
545   // FOR_REPOS(i, repo)
546   //   printf("%s: %d solvables\n", repo->name, repo->nsolvables);
547   pool_addfileprovides(pool);
548   pool_createwhatprovides(pool);
549
550   queue_init(&job);
551   for (i = 2; i < argc; i++)
552     mkselect(pool, argv[i], 0, &job);
553   if (!job.count && mode == SOLVER_UPDATE)
554     updateall = 1;
555   else if (!job.count)
556     {
557       printf("nothing matched\n");
558       exit(1);
559     }
560
561   if (!mode)
562     {
563       /* show mode, no solver needed */
564       for (i = 0; i < job.count; i += 2)
565         {
566           FOR_JOB_SELECT(p, pp, job.elements[i], job.elements[i + 1])
567             {
568               Solvable *s = pool_id2solvable(pool, p);
569               printf("  - %s [%s]\n", solvable2str(pool, s), s->repo->name);
570             }
571         }
572       exit(0);
573     }
574
575   // add mode
576   for (i = 0; i < job.count; i += 2)
577     job.elements[i] |= mode;
578
579 rerunsolver:
580   for (;;)
581     {
582       Id problem, solution;
583       int pcnt, scnt;
584
585       solv = solver_create(pool);
586       solv->ignorealreadyrecommended = 1;
587       solv->updatesystem = updateall;
588       solver_solve(solv, &job);
589       if (!solv->problems.count)
590         break;
591       pcnt = solver_problem_count(solv);
592       printf("Found %d problems:\n", pcnt);
593       for (problem = 1; problem <= pcnt; problem++)
594         {
595           int take = 0;
596           printf("Problem %d:\n", problem);
597           solver_printprobleminfo(solv, problem);
598           printf("\n");
599           scnt = solver_solution_count(solv, problem);
600           for (solution = 1; solution <= pcnt; solution++)
601             {
602               printf("Solution %d:\n", solution);
603               solver_printsolution(solv, problem, solution);
604               printf("\n");
605             }
606           for (;;)
607             {
608               printf("Please choose a solution: ");
609               fflush(stdout);
610               *inbuf = 0;
611               if (!(ip = fgets(inbuf, sizeof(inbuf), stdin)))
612                 {
613                   printf("Abort.\n");
614                   exit(1);
615                 }
616               while (*ip == ' ' || *ip == '\t')
617                 ip++;
618               if (*ip >= '0' && *ip <= '9')
619                 {
620                   take = atoi(ip);
621                   if (take >= 1 && take <= solution)
622                     break;
623                 }
624               if (*ip == 's')
625                 {
626                   take = 0;
627                   break;
628                 }
629               if (*ip == 'q')
630                 {
631                   printf("Abort.\n");
632                   exit(1);
633                 }
634             }
635           if (!take)
636             continue;
637           solver_take_solution(solv, problem, take, &job);
638         }
639       solver_free(solv);
640       solv = 0;
641     }
642   if (!solv->trans.steps.count)
643     {
644       printf("Nothing to do.\n");
645       exit(1);
646     }
647   printf("\n");
648   printf("Transaction summary:\n\n");
649   solver_printtransaction(solv);
650   if (!yesno("OK to continue (y/n)? "))
651     {
652       printf("Abort.\n");
653       exit(1);
654     }
655
656   trans = &solv->trans;
657   queue_init(&checkq);
658   newpkgs = transaction_installedresult(trans, &checkq);
659   newpkgsfps = 0;
660
661   if (newpkgs)
662     {
663       printf("Downloading %d packages\n", newpkgs);
664       newpkgsfps = sat_calloc(newpkgs, sizeof(*newpkgsfps));
665       for (i = 0; i < newpkgs; i++)
666         {
667           unsigned int medianr;
668           char *loc;
669           Solvable *s;
670           struct repoinfo *cinfo;
671
672           p = checkq.elements[i];
673           s = pool_id2solvable(pool, p);
674           cinfo = s->repo->appdata;
675           if (!cinfo)
676             {
677               printf("%s: no repository information\n", s->repo->name);
678               exit(1);
679             }
680           loc = solvable_get_location(s, &medianr);
681           if ((newpkgsfps[i] = curlfopen(cinfo->baseurl, loc, 0)) == 0)
682             {
683               printf("%s: %s not found\n", s->repo->name, loc);
684               exit(1);
685             }
686         }
687     }
688
689   if (newpkgs)
690     {
691       Queue conflicts;
692
693       printf("Searching for file conflicts\n");
694       queue_init(&conflicts);
695       fcstate.rpmdbstate = 0;
696       fcstate.newpkgscnt = newpkgs;
697       fcstate.checkq = &checkq;
698       fcstate.newpkgsfps = newpkgsfps;
699       pool_findfileconflicts(pool, &checkq, newpkgs, &conflicts, &fc_cb, &fcstate);
700       if (conflicts.count)
701         {
702           printf("\n");
703           for (i = 0; i < conflicts.count; i += 5)
704             printf("file %s of package %s conflicts with package %s\n", id2str(pool, conflicts.elements[i]), solvid2str(pool, conflicts.elements[i + 1]), solvid2str(pool, conflicts.elements[i + 3]));
705           printf("\n");
706           if (yesno("Re-run solver (y/n/q)? "))
707             {
708               for (i = 0; i < newpkgs; i++)
709                 fclose(newpkgsfps[i]);
710               newpkgsfps = sat_free(newpkgsfps);
711               solver_free(solv);
712               pool_add_fileconflicts_deps(pool, &conflicts);
713               pool_createwhatprovides(pool);    /* Hmm... */
714               goto rerunsolver;
715             }
716         }
717       queue_free(&conflicts);
718     }
719
720   printf("Committing transaction:\n\n");
721   transaction_order(trans, 0);
722   for (i = 0; i < trans->steps.count; i++)
723     {
724       char rpmname[256];
725       const char *evr, *evrp;
726       Solvable *s;
727       int j;
728       FILE *fp;
729
730       p = trans->steps.elements[i];
731       s = pool_id2solvable(pool, p);
732       Id type = transaction_type(trans, p, SOLVER_TRANSACTION_RPM_ONLY);
733       switch(type)
734         {
735         case SOLVER_TRANSACTION_ERASE:
736           printf("erase %s\n", solvid2str(pool, p));
737           if (strlen(solvid2str(pool, p)) > 255)
738             continue;
739           evr = evrp = id2str(pool, s->evr);
740           while (*evrp >= '0' && *evrp <= '9')
741             evrp++;
742           if (evrp > evr && evrp[0] == ':' && evrp[1])
743             evr = evrp + 1;
744           sprintf(rpmname, "%s-%s.%s", id2str(pool, s->name), evr, id2str(pool, s->arch));
745           runrpm("-e", rpmname, -1);
746           break;
747         case SOLVER_TRANSACTION_INSTALL:
748         case SOLVER_TRANSACTION_MULTIINSTALL:
749           printf("install %s\n", solvid2str(pool, p));
750           for (j = 0; j < newpkgs; j++)
751             if (checkq.elements[j] == p)
752               break;
753           fp = j < newpkgs ? newpkgsfps[j] : 0;
754           rewind(fp);
755           lseek(fileno(fp), 0, SEEK_SET);
756           runrpm(type == SOLVER_TRANSACTION_MULTIINSTALL ? "-i" : "-U", "/dev/fd/3", fileno(fp));
757           fclose(fp);
758           newpkgsfps[j] = 0;
759           break;
760         default:
761           break;
762         }
763     }
764
765   for (i = 0; i < newpkgs; i++)
766     if (newpkgsfps[i])
767       fclose(newpkgsfps[i]);
768   sat_free(newpkgsfps);
769   queue_free(&checkq);
770   solver_free(solv);
771   queue_free(&job);
772   pool_free(pool);
773   exit(0);
774 }