Branch data Line data Source code
1 : : /*-
2 : : * Copyright (c) 2011-2020 Baptiste Daroussin <bapt@FreeBSD.org>
3 : : * Copyright (c) 2011-2012 Julien Laffaye <jlaffaye@FreeBSD.org>
4 : : * Copyright (c) 2011 Will Andrews <will@FreeBSD.org>
5 : : * Copyright (c) 2015 Matthew Seaman <matthew@FreeBSD.org>
6 : : * All rights reserved.
7 : : *
8 : : * Redistribution and use in source and binary forms, with or without
9 : : * modification, are permitted provided that the following conditions
10 : : * are met:
11 : : * 1. Redistributions of source code must retain the above copyright
12 : : * notice, this list of conditions and the following disclaimer
13 : : * in this position and unchanged.
14 : : * 2. Redistributions in binary form must reproduce the above copyright
15 : : * notice, this list of conditions and the following disclaimer in the
16 : : * documentation and/or other materials provided with the distribution.
17 : : *
18 : : * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
19 : : * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 : : * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 : : * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
22 : : * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 : : * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 : : * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 : : * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 : : * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 : : * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 : : */
29 : :
30 : : #ifdef HAVE_CONFIG_H
31 : : #include "pkg_config.h"
32 : : #endif
33 : :
34 : : #include <sys/param.h>
35 : : #include <sys/queue.h>
36 : :
37 : : #ifdef PKG_COMPAT
38 : : #include <sys/stat.h>
39 : : #include <sys/types.h>
40 : : #include <dirent.h>
41 : : #endif
42 : :
43 : : #include <err.h>
44 : : #include <getopt.h>
45 : : #include <stdio.h>
46 : : #include <stdlib.h>
47 : : #include <pkg.h>
48 : : #include <string.h>
49 : : #include <strings.h>
50 : : #include <unistd.h>
51 : : #include <utlist.h>
52 : :
53 : : #include "pkgcli.h"
54 : :
55 : : struct pkg_entry {
56 : : struct pkg *pkg;
57 : : struct pkg_entry *next;
58 : : struct pkg_entry *prev;
59 : : };
60 : : struct pkg_entry *pkg_head = NULL;
61 : :
62 : : void
63 : 0 : usage_create(void)
64 : : {
65 : 0 : fprintf(stderr, "Usage: pkg create [-eOhnqv] [-f format] [-l level] "
66 : : "[-o outdir] [-p plist] [-r rootdir] -m metadatadir\n");
67 : 0 : fprintf(stderr, "Usage: pkg create [-eOhnqv] [-f format] [-l level] "
68 : : "[-o outdir] [-r rootdir] -M manifest\n");
69 : 0 : fprintf(stderr, " pkg create [-eOhgnqvx] [-f format] [-l level] "
70 : : "[-o outdir] [-r rootdir] pkg-name ...\n");
71 : 0 : fprintf(stderr, " pkg create [-eOhnqv] [-f format] [-l level] "
72 : : "[-o outdir] [-r rootdir] -a\n\n");
73 : 0 : fprintf(stderr, "For more information see 'pkg help create'.\n");
74 : 0 : }
75 : :
76 : : static int
77 : 0 : pkg_create_matches(int argc, char **argv, match_t match, struct pkg_create *pc)
78 : : {
79 : 0 : int i, ret = EPKG_OK, retcode = EXIT_SUCCESS;
80 : 0 : struct pkg *pkg = NULL;
81 : 0 : struct pkgdb *db = NULL;
82 : 0 : struct pkgdb_it *it = NULL;
83 : 0 : int query_flags = PKG_LOAD_DEPS | PKG_LOAD_FILES |
84 : : PKG_LOAD_CATEGORIES | PKG_LOAD_DIRS | PKG_LOAD_SCRIPTS |
85 : : PKG_LOAD_OPTIONS | PKG_LOAD_LICENSES |
86 : : PKG_LOAD_USERS | PKG_LOAD_GROUPS | PKG_LOAD_SHLIBS_REQUIRED |
87 : : PKG_LOAD_PROVIDES | PKG_LOAD_REQUIRES |
88 : : PKG_LOAD_SHLIBS_PROVIDED | PKG_LOAD_ANNOTATIONS | PKG_LOAD_LUA_SCRIPTS;
89 : 0 : struct pkg_entry *e = NULL, *etmp;
90 : : bool foundone;
91 : :
92 [ # # ]: 0 : if (pkgdb_open(&db, PKGDB_DEFAULT) != EPKG_OK) {
93 : 0 : pkgdb_close(db);
94 : 0 : return (EXIT_FAILURE);
95 : : }
96 : : /* XXX: get rid of hardcoded timeouts */
97 [ # # ]: 0 : if (pkgdb_obtain_lock(db, PKGDB_LOCK_READONLY) != EPKG_OK) {
98 : 0 : pkgdb_close(db);
99 : 0 : warnx("Cannot get a read lock on a database, it is locked by another process");
100 : 0 : return (EXIT_FAILURE);
101 : : }
102 : :
103 [ # # # # ]: 0 : for (i = 0; i < argc || match == MATCH_ALL; i++) {
104 [ # # ]: 0 : if (match == MATCH_ALL) {
105 : 0 : printf("Loading the package list...\n");
106 [ # # ]: 0 : if ((it = pkgdb_query(db, NULL, match)) == NULL)
107 : 0 : goto cleanup;
108 : 0 : match = !MATCH_ALL;
109 : 0 : } else
110 [ # # ]: 0 : if ((it = pkgdb_query(db, argv[i], match)) == NULL)
111 : 0 : goto cleanup;
112 : :
113 : 0 : foundone = false;
114 [ # # ]: 0 : while ((ret = pkgdb_it_next(it, &pkg, query_flags)) == EPKG_OK) {
115 [ # # ]: 0 : if ((e = malloc(sizeof(struct pkg_entry))) == NULL)
116 : 0 : err(1, "malloc(pkg_entry)");
117 : 0 : e->pkg = pkg;
118 : 0 : pkg = NULL;
119 [ # # ]: 0 : DL_APPEND(pkg_head, e);
120 : 0 : foundone = true;
121 : : }
122 [ # # ]: 0 : if (!foundone) {
123 : 0 : warnx("No installed package matching \"%s\" found\n",
124 : 0 : argv[i]);
125 : 0 : retcode = EXIT_FAILURE;
126 : 0 : }
127 : :
128 : 0 : pkgdb_it_free(it);
129 [ # # ]: 0 : if (ret != EPKG_END)
130 : 0 : retcode = EXIT_FAILURE;
131 : 0 : }
132 : :
133 [ # # # # ]: 0 : DL_FOREACH_SAFE(pkg_head, e, etmp) {
134 [ # # # # : 0 : DL_DELETE(pkg_head, e);
# # # # ]
135 : 0 : pkg_printf("Creating package for %n-%v\n", e->pkg, e->pkg);
136 : 0 : ret = pkg_create_i(pc, e->pkg, false);
137 [ # # ]: 0 : if (ret == EPKG_EXIST) {
138 : 0 : pkg_printf("%n-%v already packaged, skipping...\n",
139 : 0 : e->pkg, e->pkg);
140 : 0 : }
141 [ # # # # ]: 0 : if (ret != EPKG_OK && ret != EPKG_EXIST)
142 : 0 : retcode = EXIT_FAILURE;
143 : 0 : pkg_free(e->pkg);
144 : 0 : free(e);
145 : 0 : }
146 : :
147 : : cleanup:
148 : 0 : pkgdb_release_lock(db, PKGDB_LOCK_READONLY);
149 : 0 : pkgdb_close(db);
150 : :
151 : 0 : return (retcode);
152 : 0 : }
153 : :
154 : : /*
155 : : * options:
156 : : * -M: manifest file
157 : : * -f <format>: format could be tzst, txz, tgz, tbz or tar
158 : : * -g: globbing
159 : : * -h: pkg name with hash and symlink
160 : : * -m: path to dir where to find the metadata
161 : : * -o: output directory where to create packages by default ./ is used
162 : : * -q: quiet mode
163 : : * -r: rootdir for the package
164 : : * -x: regex
165 : : */
166 : :
167 : : int
168 : 1083 : exec_create(int argc, char **argv)
169 : : {
170 : : struct pkg_create *pc;
171 : 1083 : match_t match = MATCH_EXACT;
172 : 1083 : const char *outdir = NULL;
173 : 1083 : const char *format = NULL;
174 : 1083 : const char *rootdir = NULL;
175 : 1083 : const char *metadatadir = NULL;
176 : 1083 : const char *manifest = NULL;
177 : 1083 : char *plist = NULL;
178 : : char *endptr;
179 : : int ch;
180 : : int level;
181 : : int ret;
182 : 1083 : bool hash = false;
183 : 1083 : bool overwrite = true;
184 : 1083 : bool expand_manifest = false;
185 : 1083 : time_t ts = (time_t)-1;
186 : :
187 : : /* Sentinel values: INT_MIN (fast), -1 (default per pkg),
188 : : * 0 (default per libarchive), INT_MAX (best). */
189 : 1083 : level = -1;
190 : :
191 : : /* POLA: pkg create is quiet by default, unless
192 : : * PKG_CREATE_VERBOSE is set in pkg.conf. This is for
193 : : * historical reasons. */
194 : :
195 : 1083 : quiet = !pkg_object_bool(pkg_config_get("PKG_CREATE_VERBOSE"));
196 : :
197 : 1083 : struct option longopts[] = {
198 : : { "all", no_argument, NULL, 'a' },
199 : : { "expand-manifest", no_argument, NULL, 'e' },
200 : : { "format", required_argument, NULL, 'f' },
201 : : { "glob", no_argument, NULL, 'g' },
202 : : { "hash", no_argument, NULL, 'h' },
203 : : { "level", required_argument, NULL, 'l' },
204 : : { "regex", no_argument, NULL, 'x' },
205 : : { "root-dir", required_argument, NULL, 'r' },
206 : : { "metadata", required_argument, NULL, 'm' },
207 : : { "manifest", required_argument, NULL, 'M' },
208 : : { "no-clobber", no_argument, NULL, 'n' },
209 : : { "out-dir", required_argument, NULL, 'o' },
210 : : { "plist", required_argument, NULL, 'p' },
211 : : { "quiet", no_argument, NULL, 'q' },
212 : : { "timestamp", required_argument, NULL, 't' },
213 : : { "verbose", no_argument, NULL, 'v' },
214 : : { NULL, 0, NULL, 0 },
215 : : };
216 : :
217 [ + + ]: 2924 : while ((ch = getopt_long(argc, argv, "+aeghxf:l:r:m:M:no:p:qvt:", longopts, NULL)) != -1) {
218 [ - - + - : 1845 : switch (ch) {
+ - + + +
+ + + + +
- - - ]
219 : : case 'a':
220 : 0 : match = MATCH_ALL;
221 : 0 : break;
222 : : case 'e':
223 : 0 : expand_manifest = true;
224 : 0 : break;
225 : : case 'f':
226 : 12 : format = optarg;
227 : 12 : break;
228 : : case 'g':
229 : 0 : match = MATCH_GLOB;
230 : 0 : break;
231 : : case 'h':
232 : 4 : hash = true;
233 : 4 : break;
234 : : case 'l':
235 : : {
236 : : const char *errstr;
237 : :
238 : 0 : level = strtonum(optarg, -200, 200, &errstr);
239 [ # # ]: 0 : if (errstr == NULL)
240 : 0 : break;
241 [ # # ]: 0 : if (strcasecmp(optarg, "best") == 0) {
242 : 0 : level = INT_MAX;
243 : 0 : break;
244 [ # # ]: 0 : } else if (strcasecmp(optarg, "fast") == 0) {
245 : 0 : level = INT_MIN;
246 : 0 : break;
247 : : }
248 : 0 : warnx("Invalid compression level %s", optarg);
249 : 0 : return (EXIT_FAILURE);
250 : : }
251 : : case 'm':
252 : 133 : metadatadir = optarg;
253 : 133 : break;
254 : : case 'M':
255 : 946 : manifest = optarg;
256 : 946 : break;
257 : : case 'o':
258 : 317 : outdir = optarg;
259 : 317 : break;
260 : : case 'n':
261 : 4 : overwrite = false;
262 : 4 : break;
263 : : case 'p':
264 : 236 : plist = optarg;
265 : 236 : break;
266 : : case 'q':
267 : 13 : quiet = true;
268 : 13 : break;
269 : : case 'r':
270 : 168 : rootdir = optarg;
271 : 168 : break;
272 : : case 't':
273 : 12 : endptr = NULL;
274 : 12 : ts = (time_t)strtoimax(optarg, &endptr, 10);
275 [ + + ]: 12 : if (*endptr != '\0') {
276 : 4 : warnx("Invalid timestamp %s", optarg);
277 : 4 : return (EXIT_FAILURE);
278 : : }
279 : 8 : break;
280 : : case 'v':
281 : 0 : quiet = false;
282 : 0 : break;
283 : : case 'x':
284 : 0 : match = MATCH_REGEX;
285 : 0 : break;
286 : : default:
287 : 0 : usage_create();
288 : 0 : return (EXIT_FAILURE);
289 : : }
290 : : }
291 : 1079 : argc -= optind;
292 : 1079 : argv += optind;
293 : :
294 [ + - + + : 1079 : if (match != MATCH_ALL && metadatadir == NULL && manifest == NULL &&
- + # # ]
295 : 0 : argc == 0) {
296 : 0 : usage_create();
297 : 0 : return (EXIT_FAILURE);
298 : : }
299 : :
300 [ + + - + : 1079 : if (metadatadir == NULL && manifest == NULL && rootdir != NULL) {
# # ]
301 : 0 : warnx("Do not specify a rootdir without also specifying "
302 : : "either a metadatadir or manifest");
303 : 0 : usage_create();
304 : 0 : return (EXIT_FAILURE);
305 : : }
306 : :
307 [ + + ]: 1079 : if (outdir == NULL)
308 : 762 : outdir = "./";
309 : :
310 : 1079 : pc = pkg_create_new();
311 [ + + ]: 1079 : if (format != NULL) {
312 [ + - ]: 12 : if (format[0] == '.')
313 : 0 : ++format;
314 [ + - ]: 12 : if (!pkg_create_set_format(pc, format))
315 : 0 : warnx("unknown format %s, using the default", format);
316 : 12 : }
317 : 1079 : pkg_create_set_compression_level(pc, level);
318 : 1079 : pkg_create_set_overwrite(pc, overwrite);
319 : 1079 : pkg_create_set_rootdir(pc, rootdir);
320 : 1079 : pkg_create_set_output_dir(pc, outdir);
321 : 1079 : pkg_create_set_expand_manifest(pc, expand_manifest);
322 [ + + ]: 1079 : if (ts != (time_t)-1)
323 : 8 : pkg_create_set_timestamp(pc, ts);
324 : :
325 [ + + + - ]: 1079 : if (metadatadir == NULL && manifest == NULL) {
326 : 0 : ret = pkg_create_matches(argc, argv, match, pc);
327 : 0 : pkg_create_free(pc);
328 : 0 : return (ret == EPKG_OK ? EXIT_SUCCESS : EXIT_FAILURE);
329 : : }
330 [ + + ]: 1079 : ret = pkg_create(pc, metadatadir != NULL ? metadatadir : manifest, plist,
331 : 1079 : hash);
332 : 1079 : pkg_create_free(pc);
333 [ + + + + ]: 1079 : if (ret == EPKG_EXIST || ret == EPKG_OK)
334 : 998 : return (EXIT_SUCCESS);
335 : 81 : return (EXIT_FAILURE);
336 : 1083 : }
337 : :
|