Line data Source code
1 : /*-
2 : * Copyright (c) 2011-2012 Baptiste Daroussin <bapt@FreeBSD.org>
3 : * Copyright (c) 2011-2012 Julien Laffaye <jlaffaye@FreeBSD.org>
4 : * Copyright (c) 2011-2012 Marin Atanasov Nikolov <dnaeon@gmail.com>
5 : * Copyright (c) 2014 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 : #include <sys/param.h>
31 : #include <sys/stat.h>
32 :
33 : #include <err.h>
34 : #include <getopt.h>
35 : #include <pkg.h>
36 : #include <stdio.h>
37 : #include <string.h>
38 : #include <sysexits.h>
39 : #include <unistd.h>
40 : #include <kvec.h>
41 :
42 : #include "pkgcli.h"
43 :
44 : typedef kvec_t(char *) charlist;
45 :
46 : void
47 0 : usage_which(void)
48 : {
49 0 : fprintf(stderr, "Usage: pkg which [-qgop] <file>\n\n");
50 0 : fprintf(stderr, "For more information see 'pkg help which'.\n");
51 0 : }
52 :
53 : static bool is_there(char *);
54 : int get_match(char **, char **, char *);
55 :
56 : static bool
57 0 : already_in_list(charlist *list, const char *pattern)
58 : {
59 : int i;
60 :
61 0 : for (i = 0; i < kv_size(*list); i++)
62 0 : if (strcmp(kv_A(*list, i), pattern) == 0)
63 0 : return (true);
64 :
65 0 : return (false);
66 : }
67 :
68 : int
69 0 : exec_which(int argc, char **argv)
70 : {
71 0 : struct pkgdb *db = NULL;
72 0 : struct pkgdb_it *it = NULL;
73 0 : struct pkg *pkg = NULL;
74 : char pathabs[MAXPATHLEN];
75 : char *p, *path, *match, *savedpath;
76 0 : int ret = EPKG_OK, retcode = EX_SOFTWARE;
77 : int ch, i;
78 0 : int res, pathlen = 0;
79 0 : bool orig = false;
80 0 : bool glob = false;
81 0 : bool search = false;
82 0 : bool search_s = false;
83 : charlist patterns;
84 :
85 0 : struct option longopts[] = {
86 : { "glob", no_argument, NULL, 'g' },
87 : { "origin", no_argument, NULL, 'o' },
88 : { "path-search", no_argument, NULL, 'p' },
89 : { "quiet", no_argument, NULL, 'q' },
90 : { NULL, 0, NULL, 0 },
91 : };
92 :
93 0 : path = NULL;
94 :
95 0 : while ((ch = getopt_long(argc, argv, "+gopq", longopts, NULL)) != -1) {
96 0 : switch (ch) {
97 : case 'g':
98 0 : glob = true;
99 0 : break;
100 : case 'o':
101 0 : orig = true;
102 0 : break;
103 : case 'p':
104 0 : search_s = true;
105 0 : break;
106 : case 'q':
107 0 : quiet = true;
108 0 : break;
109 : default:
110 0 : usage_which();
111 0 : return (EX_USAGE);
112 : }
113 : }
114 :
115 0 : argc -= optind;
116 0 : argv += optind;
117 :
118 0 : if (argc < 1) {
119 0 : usage_which();
120 0 : return (EX_USAGE);
121 : }
122 :
123 0 : if (pkgdb_open(&db, PKGDB_DEFAULT) != EPKG_OK) {
124 0 : return (EX_IOERR);
125 : }
126 :
127 0 : if (pkgdb_obtain_lock(db, PKGDB_LOCK_READONLY) != EPKG_OK) {
128 0 : pkgdb_close(db);
129 0 : warnx("Cannot get a read lock on a database, it is locked by another process");
130 0 : return (EX_TEMPFAIL);
131 : }
132 :
133 0 : if (search_s) {
134 0 : if ((path = getenv("PATH")) == NULL) {
135 0 : printf("$PATH is not set, falling back to non-search behaviour\n");
136 0 : search_s = false;
137 : } else {
138 0 : pathlen = strlen(path) + 1;
139 : }
140 : }
141 :
142 0 : while (argc >= 1) {
143 0 : kv_init(patterns);
144 0 : retcode = EX_SOFTWARE;
145 0 : if (search_s) {
146 0 : if ((argv[0][0] == '.') || (argv[0][0] == '/')) {
147 0 : search = false;
148 : } else {
149 0 : search = true;
150 :
151 0 : if (strlen(argv[0]) >= FILENAME_MAX) {
152 0 : retcode = EX_USAGE;
153 0 : goto cleanup;
154 : }
155 :
156 0 : p = malloc(pathlen);
157 0 : if (p == NULL) {
158 0 : retcode = EX_OSERR;
159 0 : goto cleanup;
160 : }
161 0 : strlcpy(p, path, pathlen);
162 :
163 0 : match = NULL;
164 0 : savedpath=p;
165 : for (;;) {
166 0 : res = get_match(&match, &p, argv[0]);
167 0 : if (p == NULL)
168 0 : break;
169 :
170 0 : if (res == (EX_USAGE)) {
171 0 : printf("%s was not found in PATH, falling back to non-search behaviour\n", argv[0]);
172 0 : search = false;
173 0 : } else if (res == (EX_OSERR)) {
174 0 : retcode = EX_OSERR;
175 0 : free(savedpath);
176 0 : goto cleanup;
177 : } else {
178 0 : pkg_absolutepath(match, pathabs, sizeof(pathabs));
179 : /* ensure not not append twice an entry if PATH is messy */
180 0 : if (already_in_list(&patterns, pathabs))
181 0 : continue;
182 0 : kv_push(char *, patterns, strdup(pathabs));
183 0 : free(match);
184 : }
185 0 : }
186 0 : free(savedpath);
187 : }
188 : }
189 :
190 0 : if (!glob && !search) {
191 0 : pkg_absolutepath(argv[0], pathabs, sizeof(pathabs));
192 0 : kv_push(char *, patterns, strdup(pathabs));
193 0 : } else if (!search) {
194 0 : if (strlcpy(pathabs, argv[0], sizeof(pathabs)) >= sizeof(pathabs)) {
195 0 : retcode = EX_USAGE;
196 0 : goto cleanup;
197 : }
198 : }
199 :
200 :
201 0 : for (i = 0; i < kv_size(patterns); i++) {
202 0 : if ((it = pkgdb_query_which(db, kv_A(patterns, i), glob)) == NULL) {
203 0 : retcode = EX_IOERR;
204 0 : goto cleanup;
205 : }
206 :
207 0 : pkg = NULL;
208 0 : while ((ret = pkgdb_it_next(it, &pkg, PKG_LOAD_BASIC)) == EPKG_OK) {
209 0 : retcode = EX_OK;
210 0 : if (quiet && orig)
211 0 : pkg_printf("%o\n", pkg);
212 0 : else if (quiet && !orig)
213 0 : pkg_printf("%n-%v\n", pkg, pkg);
214 0 : else if (!quiet && orig)
215 0 : pkg_printf("%S was installed by package %o\n", kv_A(patterns, i), pkg);
216 0 : else if (!quiet && !orig)
217 0 : pkg_printf("%S was installed by package %n-%v\n", kv_A(patterns, i), pkg, pkg);
218 : }
219 0 : if (retcode != EX_OK && !quiet)
220 0 : printf("%s was not found in the database\n", kv_A(patterns, i));
221 :
222 0 : pkg_free(pkg);
223 0 : pkgdb_it_free(it);
224 :
225 : }
226 0 : kv_destroy(patterns);
227 :
228 0 : argc--;
229 0 : argv++;
230 :
231 : }
232 :
233 : cleanup:
234 0 : pkgdb_release_lock(db, PKGDB_LOCK_READONLY);
235 0 : pkgdb_close(db);
236 :
237 0 : return (retcode);
238 : }
239 :
240 :
241 : static bool
242 0 : is_there(char *candidate)
243 : {
244 0 : return (access(candidate, F_OK) == 0);
245 : }
246 :
247 : int
248 0 : get_match(char **pathabs, char **path, char *filename)
249 : {
250 : char candidate[PATH_MAX];
251 : const char *d;
252 : int len;
253 :
254 0 : while ((d = strsep(path, ":")) != NULL) {
255 0 : if (snprintf(candidate, sizeof(candidate), "%s/%s", d,
256 : filename) >= (int)sizeof(candidate))
257 0 : continue;
258 0 : if (is_there(candidate)) {
259 0 : len = strlen(candidate) + 1;
260 0 : *pathabs = malloc(len);
261 0 : if (*pathabs == NULL)
262 0 : return (EX_OSERR);
263 0 : strlcpy(*pathabs, candidate, len);
264 0 : return (EX_OK);
265 : }
266 : }
267 0 : return (EX_USAGE);
268 : }
|