1 /*-
2  * Copyright (c) 2012-2014 Matthew Seaman <matthew@FreeBSD.org>
3  * Copyright (c) 2015 Baptiste Daroussin <bapt@FreeBSD.org>
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer
11  *    in this position and unchanged.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19  * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #include <err.h>
29 #include <getopt.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <unistd.h>
33 
34 #include <pkg.h>
35 
36 #include "pkgcli.h"
37 
38 enum action {
39 	LOCK,
40 	UNLOCK,
41 };
42 
43 static int exec_lock_unlock(int, char**, enum action);
44 static int do_lock(struct pkgdb *db, struct pkg *pkg);
45 static int do_unlock(struct pkgdb *db, struct pkg *pkg);
46 
47 void
usage_lock(void)48 usage_lock(void)
49 {
50 	fprintf(stderr, "Usage: pkg lock [-lqy] [-a|[-Cgix] <pkg-name>]\n");
51 	fprintf(stderr, "       pkg lock --has-locked-packages\n");
52 	fprintf(stderr, "       pkg unlock [-lqy] [-a|[-Cgix] <pkg-name>]\n");
53 	fprintf(stderr, "For more information see 'pkg help lock'.\n");
54 }
55 
56 static int
do_lock(struct pkgdb * db,struct pkg * pkg)57 do_lock(struct pkgdb *db, struct pkg *pkg)
58 {
59 	if (pkg_is_locked(pkg)) {
60 		if (!quiet)
61 			pkg_printf("%n-%v: already locked\n",
62 			       pkg, pkg);
63 		return (EPKG_OK);
64 	}
65 
66 	if (!query_yesno(false, "%n-%v: lock this package? ",
67 				 pkg, pkg))
68 		return (EPKG_OK);
69 
70 	if (!quiet)
71 		pkg_printf("Locking %n-%v\n", pkg, pkg);
72 
73 	return (pkgdb_set(db, pkg, PKG_SET_LOCKED, (int)true));
74 }
75 
76 
77 static int
do_unlock(struct pkgdb * db,struct pkg * pkg)78 do_unlock(struct pkgdb *db, struct pkg *pkg)
79 {
80 	if (!pkg_is_locked(pkg)) {
81 		if (!quiet)
82 			pkg_printf("%n-%v: already unlocked\n", pkg, pkg);
83 		return (EPKG_OK);
84 	}
85 
86 	if (!query_yesno(false, "%n-%v: unlock this package? ",
87 				 pkg, pkg))
88 		return (EPKG_OK);
89 
90 	if (!quiet)
91 		pkg_printf("Unlocking %n-%v\n", pkg, pkg);
92 
93 	return (pkgdb_set(db, pkg, PKG_SET_LOCKED, (int)false));
94 }
95 
96 static int
do_lock_unlock(struct pkgdb * db,int match,const char * pkgname,enum action action)97 do_lock_unlock(struct pkgdb *db, int match, const char *pkgname,
98     enum action action)
99 {
100 	struct pkgdb_it	*it = NULL;
101 	struct pkg	*pkg = NULL;
102 	int		 retcode;
103 	int		 exitcode = EXIT_SUCCESS;
104 
105 	if (pkgdb_obtain_lock(db, PKGDB_LOCK_EXCLUSIVE) != EPKG_OK) {
106 		pkgdb_close(db);
107 		warnx("Cannot get an exclusive lock on database. "
108 		      "It is locked by another process");
109 		return (EXIT_FAILURE);
110 	}
111 
112 	if ((it = pkgdb_query(db, pkgname, match)) == NULL) {
113 		exitcode = EXIT_FAILURE;
114 		goto cleanup;
115 	}
116 
117 	while (pkgdb_it_next(it, &pkg, 0) == EPKG_OK) {
118 		if (action == LOCK)
119 			retcode = do_lock(db, pkg);
120 		else
121 			retcode = do_unlock(db, pkg);
122 
123 		if (retcode != EPKG_OK) {
124 			exitcode = EXIT_FAILURE;
125 			goto cleanup;
126 		}
127 	}
128 
129 cleanup:
130 	pkg_free(pkg);
131 	pkgdb_it_free(it);
132 
133 	pkgdb_release_lock(db, PKGDB_LOCK_EXCLUSIVE);
134 
135 	return (exitcode);
136 }
137 
138 int
exec_lock(int argc,char ** argv)139 exec_lock(int argc, char **argv)
140 {
141 	return (exec_lock_unlock(argc, argv, LOCK));
142 }
143 
144 int
exec_unlock(int argc,char ** argv)145 exec_unlock(int argc, char **argv)
146 {
147 	return (exec_lock_unlock(argc, argv, UNLOCK));
148 }
149 
150 static int
list_locked(struct pkgdb * db,bool has_locked)151 list_locked(struct pkgdb *db, bool has_locked)
152 {
153 	struct pkgdb_it	*it = NULL;
154 	struct pkg	*pkg = NULL;
155 	bool		 gotone = false;
156 
157 	if ((it = pkgdb_query_cond(db, " WHERE locked=1", NULL, MATCH_ALL)) == NULL) {
158 		pkgdb_close(db);
159 		return (EXIT_FAILURE);
160 	}
161 
162 	if (!quiet && !has_locked)
163 		printf("Currently locked packages:\n");
164 
165 	while (pkgdb_it_next(it, &pkg, PKG_LOAD_BASIC) == EPKG_OK) {
166 		gotone = true;
167 		if (has_locked)
168 			break;
169 		pkg_printf("%n-%v\n", pkg, pkg);
170 	}
171 
172 	pkg_free(pkg);
173 	pkgdb_it_free(it);
174 
175 	if (has_locked)
176 		return (gotone ? EXIT_SUCCESS : EXIT_FAILURE);
177 
178 	return (EXIT_SUCCESS);
179 }
180 
181 static int
exec_lock_unlock(int argc,char ** argv,enum action action)182 exec_lock_unlock(int argc, char **argv, enum action action)
183 {
184 	struct pkgdb	*db = NULL;
185 	int		 match = MATCH_EXACT;
186 	int		 retcode, i;
187 	int		 exitcode = EXIT_SUCCESS;
188 	int		 ch;
189 	bool		 show_locked = false;
190 	bool		 read_only = false;
191 	bool		 has_locked_packages = false;
192 
193 	struct option longopts[] = {
194 		{ "all",		no_argument,	NULL,	'a' },
195 		{ "case-sensitive",	no_argument,	NULL,	'C' },
196 		{ "glob",		no_argument,	NULL,	'g' },
197 		{ "show-locked",	no_argument,	NULL,	'l' },
198 		{ "quiet",		no_argument,	NULL,	'q' },
199 		{ "regex",		no_argument,	NULL,	'x' },
200 		{ "yes",		no_argument,	NULL,	'y' },
201 		{ "has-locked-packages",no_argument,	NULL,	1 },
202 		{ NULL,		0,			NULL,	0   },
203 	};
204 
205 	while ((ch = getopt_long(argc, argv, "+aCgilqxy", longopts, NULL)) != -1) {
206 		switch (ch) {
207 		case 'a':
208 			match = MATCH_ALL;
209 			break;
210 		case 'C':
211 			pkgdb_set_case_sensitivity(true);
212 			break;
213 		case 'g':
214 			match = MATCH_GLOB;
215 			break;
216 		case 'i':
217 			pkgdb_set_case_sensitivity(false);
218 			break;
219 		case 'l':
220 			show_locked = true;
221 			break;
222 		case 'q':
223 			quiet = true;
224 			break;
225 		case 'x':
226 			match = MATCH_REGEX;
227 			break;
228 		case 'y':
229 			yes = true;
230 			break;
231 		case 1:
232 			show_locked = true;
233 			has_locked_packages = true;
234 			break;
235 		default:
236 			usage_lock();
237 			return (EXIT_FAILURE);
238 		}
239         }
240 	argc -= optind;
241 	argv += optind;
242 
243 	/* Allow 'pkg lock -l' (or 'pkg unlock -l') without any
244 	 * package arguments to just display what packages are
245 	 * currently locked.  In this case, we only need a read_only
246 	 * connection to the DB. */
247 
248 	if (show_locked && match != MATCH_ALL && argc == 0)
249 		read_only = true;
250 
251 	if (!show_locked && match != MATCH_ALL && argc == 0) {
252 		usage_lock();
253 		return (EXIT_FAILURE);
254 	}
255 
256 	if (read_only)
257 		retcode = pkgdb_access(PKGDB_MODE_READ, PKGDB_DB_LOCAL);
258 	else
259 		retcode = pkgdb_access(PKGDB_MODE_READ|PKGDB_MODE_WRITE,
260 			       PKGDB_DB_LOCAL);
261 	if (retcode == EPKG_ENODB) {
262 		if (match == MATCH_ALL)
263 			return (EXIT_SUCCESS);
264 		if (!quiet)
265 			warnx("No packages installed.  Nothing to do!");
266 		return (EXIT_SUCCESS);
267 	} else if (retcode == EPKG_ENOACCESS) {
268 		warnx("Insufficient privileges to modify the package database");
269 		return (EXIT_FAILURE);
270 	} else if (retcode != EPKG_OK) {
271 		warnx("Error accessing the package database");
272 		return (EXIT_FAILURE);
273 	}
274 
275 	retcode = pkgdb_open(&db, PKGDB_DEFAULT);
276 	if (retcode != EPKG_OK)
277 		return (EXIT_FAILURE);
278 
279 	if (!read_only) {
280 		if (match == MATCH_ALL) {
281 			exitcode = do_lock_unlock(db, match, NULL, action);
282 		} else {
283 			for (i = 0; i < argc; i++) {
284 				retcode = do_lock_unlock(db, match, argv[i], action);
285 				if (retcode != EXIT_SUCCESS)
286 					exitcode = retcode;
287 			}
288 		}
289 	}
290 
291 	if (show_locked)
292 		exitcode = list_locked(db, has_locked_packages);
293 
294 	pkgdb_close(db);
295 
296 	return (exitcode == EPKG_OK ? EXIT_SUCCESS : EXIT_FAILURE);
297 }
298