installer: Move the installer from contrib/ to usr.sbin/.
[dragonfly.git] / usr.sbin / installer / libinstaller / package.c
1 /*
2  * Copyright (c)2004 The DragonFly Project.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  *   Redistributions of source code must retain the above copyright
9  *   notice, this list of conditions and the following disclaimer.
10  *
11  *   Redistributions in binary form must reproduce the above copyright
12  *   notice, this list of conditions and the following disclaimer in
13  *   the documentation and/or other materials provided with the
14  *   distribution.
15  *
16  *   Neither the name of the DragonFly Project nor the names of its
17  *   contributors may be used to endorse or promote products derived
18  *   from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
25  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
29  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
31  * OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33
34 /*
35  * package.c
36  * Manage installation etc of packages.
37  * $Id: package.c,v 1.24 2005/02/06 21:05:18 cpressey Exp $
38  */
39
40 #include <ctype.h>
41 #include <stdarg.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45
46 #include "libaura/dict.h"
47 #include "libaura/fspred.h"
48 #include "libaura/popen.h"
49
50 #include "libdfui/dfui.h"
51
52 #include "commands.h"
53 #include "diskutil.h"
54 #include "functions.h"
55 #include "uiutil.h"
56 #include "package.h"
57
58 /*
59  * Determine whether a package is installed on a DragonFly HDD.
60  */
61 int
62 pkg_exists(struct i_fn_args *a, const char *pkg)
63 {
64         return(is_dir("%smnt/var/db/pkg/%s", a->os_root, pkg));
65 }
66
67 int
68 pkg_clean(struct i_fn_args *a, struct commands *cmds)
69 {
70         command_add(cmds, "%s%s %smnt/ /%s '*'",
71             a->os_root, cmd_name(a, "CHROOT"),
72             a->os_root, cmd_name(a, "PKG_DELETE"));
73         return(1);
74 }
75
76 /*
77  * Copy a package from an installation CD onto a DragonFly HDD.
78  */
79 int
80 pkg_copy(struct i_fn_args *a, struct commands *cmds, const char *pkg_name,
81          struct aura_dict *seen)
82 {
83         FILE *pipe;
84         char *rpkg_name;
85         char line[256];
86         char pkg_suffix[256];
87
88         /*
89          * Get all the packages that this package depends on, and
90          * recursively pkg_copy them first, if they're not already there.
91          *
92          * XXX We should be sending this command through a command chain
93          * right now so that we can get an accurate idea of what is being
94          * run and so that it will be logged.  But unfortunately that's
95          * not feasible, since this function is building another command
96          * chain for later use.  So we use a pipe.
97          */
98         if ((pipe = aura_popen("%s%s -r %s", "r",
99             a->os_root, cmd_name(a, "PKG_INFO"), pkg_name)) == NULL)
100                 return(0);
101
102         while (fgets(line, 255, pipe) != NULL) {
103                 /*
104                  * Only look at lines that begin with 'Dependency:'.
105                  */
106                 if (strncmp(line, "Dependency:", 11) != 0)
107                         continue;
108                 rpkg_name = &line[12];
109
110                 /*
111                  * Strip any trailing whitespace.
112                  */
113                 while (strlen(rpkg_name) > 0 &&
114                        isspace(rpkg_name[strlen(rpkg_name) - 1])) {
115                         rpkg_name[strlen(rpkg_name) - 1] = '\0';
116                 }
117
118                 if (!pkg_exists(a, rpkg_name)) {
119                         if (!pkg_copy(a, cmds, rpkg_name, seen)) {
120                                 aura_pclose(pipe);
121                                 return(0);
122                         }
123                 }
124         }
125         aura_pclose(pipe);
126         snprintf(pkg_suffix, 256, "tgz");
127         if (!pkg_exists(a, pkg_name) &&
128             !aura_dict_exists(seen, pkg_name, strlen(pkg_name))) {
129                 aura_dict_store(seen,
130                     pkg_name, strlen(pkg_name), "", 0);
131                 command_add(cmds, "%s%s -b %s %smnt/tmp/%s.%s",
132                     a->os_root, cmd_name(a, "PKG_CREATE"),
133                     pkg_name, a->os_root, pkg_name, pkg_suffix);
134                 command_add(cmds, "%s%s %smnt/ /%s /tmp/%s.%s",
135                     a->os_root, cmd_name(a, "CHROOT"),
136                     a->os_root, cmd_name(a, "PKG_ADD"),
137                     pkg_name, pkg_suffix);
138                 command_add(cmds, "%s%s %smnt/tmp/%s.%s",
139                     a->os_root, cmd_name(a, "RM"),
140                     a->os_root, pkg_name, pkg_suffix);
141         }
142
143         return(1);
144 }
145
146 /*
147  * Remove a package from a DragonFly HDD.
148  */
149 int
150 pkg_remove(struct i_fn_args *a, struct commands *cmds, const char *pkg_name,
151            struct aura_dict *seen)
152 {
153         FILE *pipe;
154         char *command, *rpkg_name;
155         char line[256];
156         int seen_required_by = 0;
157
158         /*
159          * Get all the packages that this package depends on, and
160          * recursively pkg_copy them first, if they're not already there.
161          */
162         asprintf(&command,
163             "%s%s %smnt/ /%s -R %s",
164             a->os_root, cmd_name(a, "CHROOT"),
165             a->os_root, cmd_name(a, "PKG_INFO"),
166             pkg_name);
167         pipe = popen(command, "r");
168         free(command);
169         if (pipe == NULL)
170                 return(0);
171
172         while (fgets(line, 255, pipe) != NULL) {
173                 /*
174                  * Only look at lines that follow the "Required by:" line.
175                  */
176                 if (seen_required_by) {
177                         rpkg_name = line;
178                         /*
179                          * Strip any trailing whitespace.
180                          */
181                         while (strlen(rpkg_name) > 0 &&
182                                isspace(rpkg_name[strlen(rpkg_name) - 1])) {
183                                 rpkg_name[strlen(rpkg_name) - 1] = '\0';
184                         }
185
186                         if (strlen(rpkg_name) > 0 && pkg_exists(a, rpkg_name)) {
187                                 if (!pkg_remove(a, cmds, rpkg_name, seen)) {
188                                         pclose(pipe);
189                                         return(0);
190                                 }
191                         }
192                 } else {
193                         if (strncmp(line, "Required by:", 12) != 0) {
194                                 seen_required_by = 1;
195                         }
196                 }
197         }
198         pclose(pipe);
199
200         if (pkg_exists(a, pkg_name) &&
201             !aura_dict_exists(seen, pkg_name, strlen(pkg_name))) {
202                 aura_dict_store(seen,
203                     pkg_name, strlen(pkg_name), "", 0);
204                 command_add(cmds, "%s%s %smnt/ /%s %s",
205                     a->os_root, cmd_name(a, "CHROOT"),
206                     a->os_root, cmd_name(a, "PKG_DELETE"),
207                     pkg_name);
208         }
209
210         return(1);
211 }