Branch data Line data Source code
1 : : /*-
2 : : * Copyright (c) 2012 Baptiste Daroussin <bapt@FreeBSD.org>
3 : : * Copyright (c) 2014 Matthew Seaman <matthew@FreeBSD.org>
4 : : * Copyright (c) 2014 Vsevolod Stakhov <vsevolod@FreeBSD.org>
5 : : * All rights reserved.
6 : : *
7 : : * Redistribution and use in source and binary forms, with or without
8 : : * modification, are permitted provided that the following conditions
9 : : * are met:
10 : : * 1. Redistributions of source code must retain the above copyright
11 : : * notice, this list of conditions and the following disclaimer
12 : : * in this position and unchanged.
13 : : * 2. Redistributions in binary form must reproduce the above copyright
14 : : * notice, this list of conditions and the following disclaimer in the
15 : : * documentation and/or other materials provided with the distribution.
16 : : *
17 : : * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
18 : : * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 : : * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 : : * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
21 : : * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 : : * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 : : * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 : : * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 : : * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 : : * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 : : */
28 : :
29 : : #include <err.h>
30 : : #include <getopt.h>
31 : : #include <stdio.h>
32 : : #include <stdbool.h>
33 : : #include <stdlib.h>
34 : : #include <string.h>
35 : : #include <unistd.h>
36 : :
37 : : #include <pkg.h>
38 : :
39 : : #include <xmalloc.h>
40 : :
41 : : #include <bsd_compat.h>
42 : :
43 : : #include "pkgcli.h"
44 : :
45 : : #define AUTOMATIC 1U<<0
46 : : #define ORIGIN 1U<<1
47 : : #define NAME 1U<<2
48 : : #define VITAL 1U<<3
49 : :
50 : : void
51 : 0 : usage_set(void)
52 : : {
53 : 0 : fprintf(stderr, "Usage: pkg set [-a] [-A 0|1] [-o <oldorigin>:<neworigin>] [-n <oldname>:<newname>] [-y] [-Cgix] [-v 0|1] <pkg-name>\n\n");
54 : 0 : fprintf(stderr, "For more information see 'pkg help set'. \n");
55 : 0 : }
56 : :
57 : : static bool
58 : 8 : check_change_values(const char *opt, char **oldv, char **newv, char guard)
59 : : {
60 : : const char *semicolon;
61 : :
62 [ + - ]: 8 : if (opt == NULL)
63 : 0 : return (false);
64 : :
65 : 8 : semicolon = strrchr(opt, ':');
66 : :
67 [ + - ]: 8 : if (semicolon == NULL)
68 : 0 : return (false);
69 : :
70 : 8 : *oldv = xmalloc(semicolon - opt + 1);
71 : 8 : strlcpy(*oldv, opt, semicolon - opt + 1);
72 : 8 : *newv = xstrdup(semicolon + 1);
73 : :
74 [ + + ]: 8 : if (guard != '\0') {
75 : : /* Check guard symbol in both new and old values */
76 [ + - + - ]: 4 : if (strrchr(*oldv, guard) == NULL ||
77 : 4 : strrchr(*newv, guard) == NULL) {
78 : 0 : free(*oldv);
79 : 0 : free(*newv);
80 : 0 : *oldv = NULL;
81 : 0 : *newv = NULL;
82 : :
83 : 0 : return (false);
84 : : }
85 : 4 : }
86 : :
87 : 8 : return (true);
88 : 8 : }
89 : :
90 : : int
91 : 40 : exec_set(int argc, char **argv)
92 : : {
93 : 40 : struct pkgdb *db = NULL;
94 : 40 : struct pkgdb_it *it = NULL;
95 : 40 : struct pkg *pkg = NULL;
96 : : int ch;
97 : : int i;
98 : 40 : match_t match = MATCH_EXACT;
99 : 40 : int64_t newautomatic = -1;
100 : 40 : int64_t newvital = -1;
101 : 40 : bool automatic = false;
102 : 40 : bool rc = false;
103 : 40 : bool vital = false;
104 : 40 : const char *changed = NULL;
105 : 40 : char *newvalue = NULL;
106 : 40 : char *oldvalue = NULL;
107 : 40 : unsigned int loads = PKG_LOAD_BASIC;
108 : 40 : unsigned int sets = 0;
109 : 40 : unsigned int field = 0, depfield = 0;
110 : : int retcode;
111 : :
112 : 40 : struct option longopts[] = {
113 : : { "automatic", required_argument, NULL, 'A' },
114 : : { "all", no_argument, NULL, 'a' },
115 : : { "case-sensitive", no_argument, NULL, 'C' },
116 : : { "glob", no_argument, NULL, 'g' },
117 : : { "case-insensitive", no_argument, NULL, 'i' },
118 : : { "change-origin", required_argument, NULL, 'o' },
119 : : { "change-name", required_argument, NULL, 'n' },
120 : : { "regex", no_argument, NULL, 'x' },
121 : : { "vital", required_argument, NULL, 'v' },
122 : : { "yes", no_argument, NULL, 'y' },
123 : : { NULL, 0, NULL, 0 },
124 : : };
125 : :
126 [ + + ]: 88 : while ((ch = getopt_long(argc, argv, "+A:aCgio:xyn:v:", longopts, NULL)) != -1) {
127 [ + - - - : 48 : switch (ch) {
- + + - +
+ - ]
128 : : case 'A':
129 : 8 : sets |= AUTOMATIC;
130 : 8 : newautomatic = optarg[0] - '0';
131 [ + + - + ]: 8 : if (newautomatic != 0 && newautomatic != 1)
132 : 0 : errx(EXIT_FAILURE, "Wrong value for -A. "
133 : : "Expecting 0 or 1, got: %s",
134 : 0 : optarg);
135 : 8 : break;
136 : : case 'a':
137 : 0 : match = MATCH_ALL;
138 : 0 : break;
139 : : case 'C':
140 : 0 : pkgdb_set_case_sensitivity(true);
141 : 0 : break;
142 : : case 'g':
143 : 0 : match = MATCH_GLOB;
144 : 0 : break;
145 : : case 'i':
146 : 0 : pkgdb_set_case_sensitivity(false);
147 : 0 : break;
148 : : case 'o':
149 : 4 : sets |= ORIGIN;
150 : 4 : loads |= PKG_LOAD_DEPS;
151 : 4 : match = MATCH_ALL;
152 : 4 : changed = "origin";
153 [ + - ]: 4 : if (!check_change_values(optarg, &oldvalue, &newvalue, '/')) {
154 : 0 : errx(EXIT_FAILURE, "Wrong format for -o. "
155 : : "Expecting oldorigin:neworigin, got: %s",
156 : 0 : optarg);
157 : : }
158 : 4 : break;
159 : : case 'n':
160 : 4 : sets |= NAME;
161 : 4 : loads |= PKG_LOAD_DEPS;
162 : 4 : match = MATCH_ALL;
163 : 4 : changed = "name";
164 [ + - ]: 4 : if (!check_change_values(optarg, &oldvalue, &newvalue, '\0')) {
165 : 0 : errx(EXIT_FAILURE, "Wrong format for -n. "
166 : : "Expecting oldname:newname, got: %s",
167 : 0 : optarg);
168 : : }
169 : 4 : break;
170 : : case 'x':
171 : 0 : match = MATCH_REGEX;
172 : 0 : break;
173 : : case 'v':
174 : 8 : sets |= VITAL;
175 : 8 : newvital = optarg[0] - '0';
176 [ + + - + ]: 8 : if (newvital != 0 && newvital != 1)
177 : 0 : errx(EXIT_FAILURE, "Wrong value for -v. "
178 : : "Expecting 0 or 1, got: %s",
179 : 0 : optarg);
180 : 8 : break;
181 : : case 'y':
182 : 24 : yes = true;
183 : 24 : break;
184 : : default:
185 : 0 : free(oldvalue);
186 : 0 : free(newvalue);
187 : 0 : usage_set();
188 : 0 : return (EXIT_FAILURE);
189 : : }
190 : : }
191 : :
192 : 40 : argc -= optind;
193 : 40 : argv += optind;
194 : :
195 [ + + + + ]: 72 : if ((argc < 1 && match != MATCH_ALL) ||
196 [ + + + + ]: 32 : (newautomatic == -1 && newvital == -1 && newvalue == NULL) ||
197 : 32 : (sets & (NAME|ORIGIN)) == (NAME|ORIGIN)) {
198 : 16 : free(newvalue);
199 : 16 : usage_set();
200 : 16 : return (EXIT_FAILURE);
201 : : }
202 : :
203 [ + + ]: 24 : if (sets & NAME) {
204 : 4 : field = PKG_SET_NAME;
205 : 4 : depfield = PKG_SET_DEPNAME;
206 : 4 : }
207 [ + + ]: 20 : else if (sets & ORIGIN) {
208 : 4 : field = PKG_SET_ORIGIN;
209 : 4 : depfield = PKG_SET_DEPORIGIN;
210 : 4 : }
211 : :
212 : 24 : retcode = pkgdb_access(PKGDB_MODE_READ|PKGDB_MODE_WRITE,
213 : : PKGDB_DB_LOCAL);
214 [ - + ]: 24 : if (retcode == EPKG_ENODB) {
215 : 0 : free(newvalue);
216 [ # # ]: 0 : if (match == MATCH_ALL)
217 : 0 : return (EXIT_SUCCESS);
218 [ # # ]: 0 : if (!quiet)
219 : 0 : warnx("No packages installed. Nothing to do!");
220 : 0 : return (EXIT_SUCCESS);
221 [ - + ]: 24 : } else if (retcode == EPKG_ENOACCESS) {
222 : 0 : free(newvalue);
223 : 0 : warnx("Insufficient privileges to modify the package database");
224 : 0 : return (EXIT_FAILURE);
225 [ - + ]: 24 : } else if (retcode != EPKG_OK) {
226 : 0 : warnx("Error accessing package database");
227 : 0 : free(newvalue);
228 : 0 : return (EXIT_FAILURE);
229 : : }
230 : :
231 [ - + ]: 24 : if (pkgdb_open(&db, PKGDB_DEFAULT) != EPKG_OK) {
232 : 0 : free(newvalue);
233 : 0 : return (EXIT_FAILURE);
234 : : }
235 : :
236 [ - + ]: 24 : if (pkgdb_obtain_lock(db, PKGDB_LOCK_EXCLUSIVE) != EPKG_OK) {
237 : 0 : pkgdb_close(db);
238 : 0 : free(newvalue);
239 : 0 : warnx("Cannot get an exclusive lock on a database, it is locked by another process");
240 : 0 : return (EXIT_FAILURE);
241 : : }
242 : :
243 [ - + ]: 24 : if (pkgdb_transaction_begin(db, NULL) != EPKG_OK) {
244 : 0 : pkgdb_close(db);
245 : 0 : free(newvalue);
246 : 0 : warnx("Cannot start transaction for update");
247 : 0 : return (EXIT_FAILURE);
248 : : }
249 : :
250 : :
251 [ + + ]: 24 : if (oldvalue != NULL) {
252 : 8 : match = MATCH_ALL;
253 [ + - ]: 8 : if ((it = pkgdb_query(db, oldvalue, MATCH_EXACT)) == NULL) {
254 : 0 : retcode = EXIT_FAILURE;
255 : 0 : goto cleanup;
256 : : }
257 : :
258 [ + - ]: 8 : if (pkgdb_it_next(it, &pkg, PKG_LOAD_BASIC) != EPKG_OK) {
259 : 0 : pkg = NULL;
260 : : /* fprintf(stderr, "%s not installed\n", oldorigin);
261 : : free(oldorigin);
262 : : pkgdb_it_free(it);
263 : : pkgdb_close(db);
264 : : return (EXIT_FAILURE);*/
265 : 0 : }
266 : :
267 : 8 : rc = yes;
268 [ + - ]: 8 : if (!yes) {
269 [ # # ]: 0 : if (pkg != NULL)
270 : 0 : rc = query_yesno(false, "Change %S from %S to %S for %n-%v? ",
271 : 0 : changed, oldvalue, newvalue, pkg, pkg);
272 : : else
273 : 0 : rc = query_yesno(false, "Change %S from %S to %S for all dependencies? ",
274 : 0 : changed, oldvalue, newvalue);
275 : 0 : }
276 [ + - - + ]: 8 : if (pkg != NULL && rc) {
277 [ - + ]: 8 : if (pkgdb_set(db, pkg, field, newvalue) != EPKG_OK) {
278 : 0 : retcode = EXIT_FAILURE;
279 : 0 : goto cleanup;
280 : : }
281 : 8 : }
282 : 8 : pkgdb_it_free(it);
283 : 8 : }
284 : 24 : i = 0;
285 : 24 : do {
286 : 24 : bool saved_rc = rc;
287 : 24 : bool gotone = false;
288 : :
289 [ + - ]: 24 : if ((it = pkgdb_query(db, argv[i], match)) == NULL) {
290 : 0 : retcode = EXIT_FAILURE;
291 : 0 : goto cleanup;
292 : : }
293 : :
294 [ + + ]: 48 : while (pkgdb_it_next(it, &pkg, loads) == EPKG_OK) {
295 : 24 : gotone = true;
296 [ + + ]: 24 : if ((sets & AUTOMATIC) == AUTOMATIC) {
297 : 8 : pkg_get(pkg, PKG_AUTOMATIC, &automatic);
298 [ - + ]: 8 : if (automatic == newautomatic)
299 : 0 : continue;
300 [ - + ]: 8 : if (!rc) {
301 [ + + ]: 8 : if (newautomatic)
302 : 4 : rc = query_yesno(false,
303 : : "Mark %n-%v as automatically installed? ",
304 : 4 : pkg, pkg);
305 : : else
306 : 4 : rc = query_yesno(false,
307 : : "Mark %n-%v as not automatically installed? ",
308 : 4 : pkg, pkg);
309 : 8 : }
310 [ - + ]: 8 : if (rc)
311 : 8 : pkgdb_set(db, pkg, PKG_SET_AUTOMATIC, (int)newautomatic);
312 : 8 : rc = saved_rc;
313 : 8 : }
314 [ + + ]: 24 : if ((sets & VITAL) == VITAL) {
315 : 8 : pkg_get(pkg, PKG_VITAL, &vital);
316 [ - + ]: 8 : if (vital == newvital)
317 : 0 : continue;
318 [ - + ]: 8 : if (!rc) {
319 [ + + ]: 8 : if (newvital)
320 : 4 : rc = query_yesno(false,
321 : : "Mark %n-%v as vital? ",
322 : 4 : pkg, pkg);
323 : : else
324 : 4 : rc = query_yesno(false,
325 : : "Mark %n-%v as not vital? ",
326 : 4 : pkg, pkg);
327 : 8 : }
328 [ - + ]: 8 : if (rc)
329 : 8 : pkgdb_set(db, pkg, PKG_SET_VITAL, (int)newvital);
330 : 8 : rc = saved_rc;
331 : 8 : }
332 [ + + ]: 24 : if (sets & (ORIGIN|NAME)) {
333 : 8 : struct pkg_dep *d = NULL;
334 [ - + ]: 8 : while (pkg_deps(pkg, &d) == EPKG_OK) {
335 : : /*
336 : : * Do not query user when he has already
337 : : * been queried.
338 : : */
339 [ # # ]: 0 : if (pkgdb_set(db, pkg, depfield, oldvalue, newvalue) != EPKG_OK) {
340 : 0 : retcode = EXIT_FAILURE;
341 : 0 : goto cleanup;
342 : : }
343 : : }
344 : 8 : }
345 : : }
346 [ + - ]: 24 : if (!gotone) {
347 : 0 : warnx("No package(s) matching %s", argv[i]);
348 : 0 : retcode = EXIT_FAILURE;
349 : 0 : }
350 : 24 : pkgdb_it_free(it);
351 : 24 : i++;
352 [ - + ]: 24 : } while (i < argc);
353 : :
354 : : cleanup:
355 : 24 : free(oldvalue);
356 : 24 : free(newvalue);
357 : 24 : pkg_free(pkg);
358 : :
359 [ - + ]: 24 : if (retcode == 0) {
360 : 24 : pkgdb_transaction_commit(db, NULL);
361 : 24 : }
362 : : else {
363 : 0 : pkgdb_transaction_rollback(db, NULL);
364 : : }
365 : :
366 : 24 : pkgdb_release_lock(db, PKGDB_LOCK_EXCLUSIVE);
367 : 24 : pkgdb_close(db);
368 : :
369 : 24 : return (retcode);
370 : 24 : }
|