Branch data Line data Source code
1 : : /*-
2 : : * Copyright (c) 2011-2013 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) 2011-2012 Marin Atanasov Nikolov <dnaeon@gmail.com>
6 : : * Copyright (c) 2014-2015 Matthew Seaman <matthew@FreeBSD.org>
7 : : * Copyright (c) 2014 Vsevolod Stakhov <vsevolod@FreeBSD.org>
8 : : * All rights reserved.
9 : : *
10 : : * Redistribution and use in source and binary forms, with or without
11 : : * modification, are permitted provided that the following conditions
12 : : * are met:
13 : : * 1. Redistributions of source code must retain the above copyright
14 : : * notice, this list of conditions and the following disclaimer
15 : : * in this position and unchanged.
16 : : * 2. Redistributions in binary form must reproduce the above copyright
17 : : * notice, this list of conditions and the following disclaimer in the
18 : : * documentation and/or other materials provided with the distribution.
19 : : *
20 : : * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
21 : : * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22 : : * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23 : : * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
24 : : * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25 : : * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 : : * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 : : * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 : : * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29 : : * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 : : */
31 : :
32 : : #ifdef HAVE_CONFIG_H
33 : : #include "pkg_config.h"
34 : : #endif
35 : :
36 : : #include <sys/param.h>
37 : :
38 : : #include <sys/stat.h>
39 : : #include <sys/queue.h>
40 : : #include <sys/types.h>
41 : : #include <sys/wait.h>
42 : : #ifdef __FreeBSD__
43 : : #include <sys/sysctl.h>
44 : : #include <sys/user.h>
45 : : #include <sys/proc.h>
46 : : #endif
47 : :
48 : : #include <assert.h>
49 : : #include <ctype.h>
50 : : #include <err.h>
51 : : #include <errno.h>
52 : : #include <getopt.h>
53 : : #include <inttypes.h>
54 : : #include <stdio.h>
55 : : #include <stdlib.h>
56 : : #include <string.h>
57 : : #include <utlist.h>
58 : : #include <unistd.h>
59 : : #ifdef HAVE_LIBJAIL
60 : : #include <jail.h>
61 : : #include <sys/jail.h>
62 : : #endif
63 : : #include <signal.h>
64 : :
65 : : #include <pkg.h>
66 : :
67 : : #include "pkgcli.h"
68 : :
69 : : /* Used to define why do we show usage message to a user */
70 : : enum pkg_usage_reason {
71 : : PKG_USAGE_ERROR,
72 : : PKG_USAGE_UNKNOWN_COMMAND,
73 : : PKG_USAGE_INVALID_ARGUMENTS,
74 : : PKG_USAGE_HELP
75 : : };
76 : :
77 : : static void usage(const char *, const char *, FILE *, enum pkg_usage_reason, ...);
78 : : static void usage_help(void);
79 : : static int exec_help(int, char **);
80 : :
81 : : static struct commands {
82 : : const char * const name;
83 : : const char * const desc;
84 : : int (*exec)(int argc, char **argv);
85 : : void (* const usage)(void);
86 : : } cmd[] = {
87 : : { "add", "Compatibility interface to install a package", exec_add, usage_add},
88 : : { "alias", "List the command line aliases", exec_alias, usage_alias},
89 : : { "annotate", "Add, modify or delete tag-value style annotations on packages", exec_annotate, usage_annotate},
90 : : { "audit", "Reports vulnerable packages", exec_audit, usage_audit},
91 : : { "autoremove", "Removes orphan packages", exec_autoremove, usage_autoremove},
92 : : { "backup", "Backs-up and restores the local package database", exec_backup, usage_backup},
93 : : { "check", "Checks for missing dependencies and database consistency", exec_check, usage_check},
94 : : { "clean", "Cleans old packages from the cache", exec_clean, usage_clean},
95 : : { "config", "Display the value of the configuration options", exec_config, usage_config},
96 : : { "create", "Creates software package distributions", exec_create, usage_create},
97 : : { "delete", "Deletes packages from the database and the system", exec_delete, usage_delete},
98 : : { "fetch", "Fetches packages from a remote repository", exec_fetch, usage_fetch},
99 : : { "help", "Displays help information", exec_help, usage_help},
100 : : { "info", "Displays information about installed packages", exec_info, usage_info},
101 : : { "install", "Installs packages from remote package repositories and local archives", exec_install, usage_install},
102 : : { "lock", "Locks package against modifications or deletion", exec_lock, usage_lock},
103 : : { "plugins", "Manages plugins and displays information about plugins", exec_plugins, usage_plugins},
104 : : { "query", "Queries information about installed packages", exec_query, usage_query},
105 : : { "register", "Registers a package into the local database", exec_register, usage_register},
106 : : { "remove", "Deletes packages from the database and the system", exec_delete, usage_delete},
107 : : { "repo", "Creates a package repository catalogue", exec_repo, usage_repo},
108 : : { "rquery", "Queries information in repository catalogues", exec_rquery, usage_rquery},
109 : : { "search", "Performs a search of package repository catalogues", exec_search, usage_search},
110 : : { "set", "Modifies information about packages in the local database", exec_set, usage_set},
111 : : { "ssh", "Package server (to be used via ssh)", exec_ssh, usage_ssh},
112 : : { "shell", "Opens a debug shell", exec_shell, usage_shell},
113 : : { "shlib", "Displays which packages link against a specific shared library", exec_shlib, usage_shlib},
114 : : { "stats", "Displays package database statistics", exec_stats, usage_stats},
115 : : { "triggers", "Execute deferred triggers", exec_triggers, usage_triggers},
116 : : { "unlock", "Unlocks a package, allowing modification or deletion", exec_unlock, usage_lock},
117 : : { "update", "Updates package repository catalogues", exec_update, usage_update},
118 : : { "updating", "Displays UPDATING information for a package", exec_updating, usage_updating},
119 : : { "upgrade", "Performs upgrades of packaged software distributions", exec_upgrade, usage_upgrade},
120 : : { "version", "Displays the versions of installed packages", exec_version, usage_version},
121 : : { "which", "Displays which package installed a specific file", exec_which, usage_which},
122 : : };
123 : :
124 : : static const unsigned int cmd_len = NELEM(cmd);
125 : :
126 : : struct plugcmd {
127 : : const char *name;
128 : : const char *desc;
129 : : int (*exec)(int argc, char **argv);
130 : : struct plugcmd *next;
131 : : struct plugcmd *prev;
132 : : };
133 : : static struct plugcmd *plugins = NULL;
134 : :
135 : : typedef int (register_cmd)(int idx, const char **name, const char **desc, int (**exec)(int argc, char **argv));
136 : : typedef int (nb_cmd)(void);
137 : :
138 : : static void
139 : 0 : show_command_names(void)
140 : : {
141 : : unsigned i;
142 : :
143 [ # # ]: 0 : for(i = 0; i < cmd_len; i++)
144 : 0 : printf("%s\n", cmd[i].name);
145 : :
146 : 0 : return;
147 : : }
148 : :
149 : : static void
150 : 0 : usage(const char *conffile, const char *reposdir, FILE *out, enum pkg_usage_reason reason, ...)
151 : : {
152 : : struct plugcmd *c;
153 : 0 : bool plugins_enabled = false;
154 : : unsigned int i;
155 : : const char *arg;
156 : : va_list vp;
157 : :
158 [ # # ]: 0 : if (reason == PKG_USAGE_UNKNOWN_COMMAND) {
159 : 0 : va_start(vp, reason);
160 [ # # ]: 0 : arg = va_arg(vp, const char *);
161 : 0 : va_end(vp);
162 : 0 : fprintf(out, "pkg: unknown command: %s\n", arg);
163 : 0 : goto out;
164 : : }
165 : 0 : else if (reason == PKG_USAGE_INVALID_ARGUMENTS) {
166 : 0 : va_start(vp, reason);
167 [ # # ]: 0 : arg = va_arg(vp, const char *);
168 : 0 : va_end(vp);
169 : 0 : fprintf(out, "pkg: %s\n", arg);
170 : 0 : }
171 : :
172 : : #ifdef HAVE_LIBJAIL
173 : : #define JAIL_ARG "-j <jail name or id>|"
174 : : #else
175 : : #define JAIL_ARG
176 : : #endif
177 : 0 : fprintf(out, "Usage: pkg [-v] [-d] [-l] [-N] ["JAIL_ARG"-c <chroot path>|-r <rootdir>] [-C <configuration file>] [-R <repo config dir>] [-o var=value] [-4|-6] <command> [<args>]\n");
178 [ # # ]: 0 : if (reason == PKG_USAGE_HELP) {
179 : 0 : fprintf(out, "Global options supported:\n");
180 : 0 : fprintf(out, "\t%-15s%s\n", "-d", "Increment debug level");
181 : : #ifdef HAVE_LIBJAIL
182 : 0 : fprintf(out, "\t%-15s%s\n", "-j", "Execute pkg(8) inside a jail(8)");
183 : : #endif
184 : 0 : fprintf(out, "\t%-15s%s\n", "-r", "Execute pkg(8) using relocating installation to <rootdir>");
185 : 0 : fprintf(out, "\t%-15s%s\n", "-c", "Execute pkg(8) inside a chroot(8)");
186 : 0 : fprintf(out, "\t%-15s%s\n", "-C", "Use the specified configuration file");
187 : 0 : fprintf(out, "\t%-15s%s\n", "-R", "Directory to search for individual repository configurations");
188 : 0 : fprintf(out, "\t%-15s%s\n", "-l", "List available commands and exit");
189 : 0 : fprintf(out, "\t%-15s%s\n", "-v", "Display pkg(8) version");
190 : 0 : fprintf(out, "\t%-15s%s\n", "-N", "Test if pkg(8) is activated and avoid auto-activation");
191 : 0 : fprintf(out, "\t%-15s%s\n", "-o", "Override configuration option from the command line");
192 : 0 : fprintf(out, "\t%-15s%s\n", "-4", "Only use IPv4");
193 : 0 : fprintf(out, "\t%-15s%s\n", "-6", "Only use IPv6");
194 : 0 : fprintf(out, "\nCommands supported:\n");
195 : :
196 [ # # ]: 0 : for (i = 0; i < cmd_len; i++)
197 : 0 : fprintf(out, "\t%-15s%s\n", cmd[i].name, cmd[i].desc);
198 : :
199 : 0 : if (!pkg_initialized() && pkg_ini(conffile, reposdir, 0) != EPKG_OK)
200 : 0 : errx(EXIT_FAILURE, "Cannot parse configuration file!");
201 : :
202 : 0 : plugins_enabled = pkg_object_bool(pkg_config_get("PKG_ENABLE_PLUGINS"));
203 : :
204 : 0 : if (plugins_enabled) {
205 [ # # ]: 0 : if (pkg_plugins_init() != EPKG_OK)
206 : 0 : errx(EXIT_FAILURE, "Plugins cannot be loaded");
207 : :
208 : 0 : fprintf(out, "\nCommands provided by plugins:\n");
209 : :
210 [ # # ]: 0 : DL_FOREACH(plugins, c) {
211 : 0 : fprintf(out, "\t%-15s%s\n", c->name, c->desc);
212 : 0 : }
213 : 0 : }
214 : 0 : fprintf(out, "\nFor more information on the different commands"
215 : : " see 'pkg help <command>'.\n");
216 : 0 : exit(EXIT_SUCCESS);
217 : : }
218 : :
219 : : out:
220 : 0 : fprintf(out, "\nFor more information on available commands and options see 'pkg help'.\n");
221 : 0 : exit(EXIT_FAILURE);
222 : : }
223 : :
224 : : static void
225 : 0 : usage_help(void)
226 : : {
227 : 0 : usage(NULL, NULL, stdout, PKG_USAGE_HELP);
228 : 0 : }
229 : :
230 : : static int
231 : 0 : exec_help(int argc, char **argv)
232 : : {
233 : : char *manpage;
234 : 0 : bool plugins_enabled = false;
235 : : struct plugcmd *c;
236 : : unsigned int i;
237 : : const pkg_object *all_aliases;
238 : : const pkg_object *alias;
239 : 0 : pkg_iter it = NULL;
240 : :
241 : 0 : if ((argc != 2) || (strcmp("help", argv[1]) == 0)) {
242 : 0 : usage_help();
243 : 0 : return(EXIT_FAILURE);
244 : : }
245 : :
246 [ # # ]: 0 : for (i = 0; i < cmd_len; i++) {
247 [ # # ]: 0 : if (strcmp(cmd[i].name, argv[1]) == 0) {
248 [ # # ]: 0 : if (asprintf(&manpage, "/usr/bin/man pkg-%s", cmd[i].name) == -1)
249 : 0 : errx(EXIT_FAILURE, "cannot allocate memory");
250 : :
251 : 0 : system(manpage);
252 : 0 : free(manpage);
253 : :
254 : 0 : return (0);
255 : : }
256 : 0 : }
257 : :
258 : 0 : plugins_enabled = pkg_object_bool(pkg_config_get("PKG_ENABLE_PLUGINS"));
259 : :
260 : 0 : if (plugins_enabled) {
261 [ # # ]: 0 : DL_FOREACH(plugins, c) {
262 [ # # ]: 0 : if (strcmp(c->name, argv[1]) == 0) {
263 [ # # ]: 0 : if (asprintf(&manpage, "/usr/bin/man pkg-%s", c->name) == -1)
264 : 0 : errx(EXIT_FAILURE, "cannot allocate memory");
265 : :
266 : 0 : system(manpage);
267 : 0 : free(manpage);
268 : :
269 : 0 : return (0);
270 : : }
271 : 0 : }
272 : 0 : }
273 : :
274 [ # # ]: 0 : if (strcmp(argv[1], "pkg") == 0) {
275 : 0 : system("/usr/bin/man 8 pkg");
276 : 0 : return (0);
277 [ # # ]: 0 : } else if (strcmp(argv[1], "pkg.conf") == 0) {
278 : 0 : system("/usr/bin/man 5 pkg.conf");
279 : 0 : return (0);
280 : : }
281 : :
282 : : /* Try aliases */
283 : 0 : all_aliases = pkg_config_get("ALIAS");
284 [ # # ]: 0 : while ((alias = pkg_object_iterate(all_aliases, &it))) {
285 [ # # ]: 0 : if (strcmp(argv[1], pkg_object_key(alias)) == 0) {
286 : 0 : printf("`%s` is an alias to `%s`\n", argv[1], pkg_object_string(alias));
287 : 0 : return (0);
288 : : }
289 : : }
290 : :
291 : : /* Command name not found */
292 : 0 : warnx("'%s' is not a valid command.\n", argv[1]);
293 : :
294 : 0 : fprintf(stderr, "See 'pkg help' for more information on the commands.\n");
295 : :
296 : 0 : return (EXIT_FAILURE);
297 : 0 : }
298 : :
299 : : static void
300 : 34 : show_plugin_info(void)
301 : : {
302 : : const pkg_object *conf;
303 : 34 : struct pkg_plugin *p = NULL;
304 : :
305 [ - + ]: 34 : while (pkg_plugins(&p) == EPKG_OK) {
306 : 0 : conf = pkg_plugin_conf(p);
307 : 0 : printf("Configuration for plugin: %s\n",
308 : 0 : pkg_plugin_get(p, PKG_PLUGIN_NAME));
309 : :
310 : 0 : printf("%s\n", pkg_object_dump(conf));
311 : : }
312 : 34 : }
313 : :
314 : : static void
315 : 34 : show_repository_info(void)
316 : : {
317 : : const char *mirror, *sig;
318 : 34 : struct pkg_repo *repo = NULL;
319 : :
320 : 34 : printf("\nRepositories:\n");
321 [ + + ]: 89 : while (pkg_repos(&repo) == EPKG_OK) {
322 [ - - + - ]: 55 : switch (pkg_repo_mirror_type(repo)) {
323 : : case SRV:
324 : 0 : mirror = "SRV";
325 : 0 : break;
326 : : case HTTP:
327 : 0 : mirror = "HTTP";
328 : 0 : break;
329 : : case NOMIRROR:
330 : 55 : mirror = "NONE";
331 : 55 : break;
332 : : default:
333 : 0 : mirror = "-unknown-";
334 : 0 : break;
335 : : }
336 [ - - + - ]: 55 : switch (pkg_repo_signature_type(repo)) {
337 : : case SIG_PUBKEY:
338 : 0 : sig = "PUBKEY";
339 : 0 : break;
340 : : case SIG_FINGERPRINT:
341 : 0 : sig = "FINGERPRINTS";
342 : 0 : break;
343 : : case SIG_NONE:
344 : 55 : sig = "NONE";
345 : 55 : break;
346 : : default:
347 : 0 : sig = "-unknown-";
348 : 0 : break;
349 : : }
350 : :
351 : 55 : printf(" %s: { \n %-16s: \"%s\",\n %-16s: %s,\n"
352 : : " %-16s: %u",
353 : 55 : pkg_repo_name(repo),
354 : 55 : "url", pkg_repo_url(repo),
355 : 55 : "enabled", pkg_repo_enabled(repo) ? "yes" : "no",
356 : 55 : "priority", pkg_repo_priority(repo));
357 : :
358 : 55 : if (pkg_repo_mirror_type(repo) != NOMIRROR)
359 : 0 : printf(",\n %-16s: \"%s\"",
360 : 0 : "mirror_type", mirror);
361 : 0 : if (pkg_repo_signature_type(repo) != SIG_NONE)
362 : 0 : printf(",\n %-16s: \"%s\"",
363 : 0 : "signature_type", sig);
364 : 0 : if (pkg_repo_fingerprints(repo) != NULL)
365 : 0 : printf(",\n %-16s: \"%s\"",
366 : 0 : "fingerprints", pkg_repo_fingerprints(repo));
367 : 0 : if (pkg_repo_key(repo) != NULL)
368 : 0 : printf(",\n %-16s: \"%s\"",
369 : 0 : "pubkey", pkg_repo_key(repo));
370 : 0 : if (pkg_repo_ip_version(repo) != 0)
371 : 0 : printf(",\n %-16s: %u",
372 : 0 : "ip_version", pkg_repo_ip_version(repo));
373 : 55 : printf("\n }\n");
374 : : }
375 : 34 : }
376 : :
377 : : static void
378 : 34 : show_version_info(int version)
379 : : {
380 : : char *config;
381 : 34 : if (version > 1)
382 : 34 : printf("%-24s: ", "Version");
383 : :
384 : 34 : printf(PKG_PORTVERSION""GITHASH"\n");
385 : :
386 [ - + ]: 34 : if (version == 1)
387 : 0 : exit(EXIT_SUCCESS);
388 : :
389 : 34 : config = pkg_config_dump();
390 : 34 : printf("%s\n", config);
391 : 34 : free(config);
392 : 34 : show_plugin_info();
393 : 34 : show_repository_info();
394 : :
395 : 34 : exit(EXIT_SUCCESS);
396 : : /* NOTREACHED */
397 : : }
398 : :
399 : : static void
400 : 13 : do_activation_test(int argc)
401 : : {
402 : : int count;
403 : :
404 : : /* Test to see if pkg(8) has been activated. Exit with an
405 : : error code if not. Can be combined with -c and -j to test
406 : : if pkg is activated in chroot or jail. If there are no
407 : : other arguments, and pkg(8) has been activated, show how
408 : : many packages have been installed. */
409 : :
410 [ - + - - ]: 13 : switch (pkg_status(&count)) {
411 : : case PKG_STATUS_UNINSTALLED: /* This case shouldn't ever happen... */
412 : 0 : errx(EXIT_FAILURE, "can't execute " PKG_EXEC_NAME
413 : : " or " PKG_STATIC_NAME "\n");
414 : : /* NOTREACHED */
415 : : case PKG_STATUS_NODB:
416 : 13 : errx(EXIT_FAILURE, "package database non-existent");
417 : : /* NOTREACHED */
418 : : case PKG_STATUS_NOPACKAGES:
419 : 0 : errx(EXIT_FAILURE, "no packages registered");
420 : : /* NOTREACHED */
421 : : case PKG_STATUS_ACTIVE:
422 [ # # ]: 0 : if (argc == 0) {
423 : 0 : warnx("%d packages installed", count);
424 : 0 : exit(EXIT_SUCCESS);
425 : : }
426 : 0 : break;
427 : : }
428 : 0 : return;
429 : : }
430 : :
431 : : static void
432 : 805 : export_arg_option (char *arg)
433 : : {
434 : : char *eqp;
435 : : const char *opt;
436 : :
437 : 805 : if ((eqp = strchr(arg, '=')) != NULL) {
438 : 805 : *eqp = '\0';
439 : :
440 [ + + ]: 805 : if ((opt = getenv (arg)) != NULL) {
441 : 16 : warnx("option %s is defined in the environment to '%s' but command line "
442 : 16 : "option redefines it", arg, opt);
443 : 16 : setenv(arg, eqp + 1, 1);
444 : 16 : }
445 : : else {
446 : 789 : setenv(arg, eqp + 1, 0);
447 : : }
448 : :
449 : 805 : *eqp = '=';
450 : 805 : }
451 : 805 : }
452 : :
453 : : static void
454 : 3452 : start_process_worker(char *const *save_argv)
455 : : {
456 : 3452 : int ret = EXIT_SUCCESS;
457 : : int status;
458 : : pid_t child_pid;
459 : :
460 : : /* Fork off a child process to do the actual package work.
461 : : * The child may be jailed or chrooted. If a restart is required
462 : : * (eg. pkg(8) inself was upgraded) the child can exit with
463 : : * 'EX_NEEDRESTART' and the same forking process will be
464 : : * replayed. This function returns control in the child
465 : : * process only. */
466 : :
467 : 3465 : while (1) {
468 : 3465 : child_pid = fork();
469 : :
470 [ + + ]: 3465 : if (child_pid == 0) {
471 : : /* Load the new Pkg image */
472 : 3477 : if (ret == EX_NEEDRESTART)
473 : 13 : execvp(getprogname(), save_argv);
474 : 3464 : return;
475 : : } else {
476 [ - + ]: 3465 : if (child_pid == -1)
477 : 0 : err(EXIT_FAILURE, "Failed to fork worker process");
478 : :
479 [ - + ]: 3465 : while (waitpid(child_pid, &status, 0) == -1) {
480 [ # # ]: 0 : if (errno != EINTR)
481 : 0 : err(EXIT_FAILURE, "Child process pid=%d", (int)child_pid);
482 : : }
483 : :
484 : 3465 : ret = WEXITSTATUS(status);
485 : :
486 : 3465 : if (WIFEXITED(status) && ret != EX_NEEDRESTART)
487 : 3452 : break;
488 : 13 : if (WIFSIGNALED(status)) {
489 : : /* Process got some terminating signal, hence stop the loop */
490 : 0 : fprintf(stderr, "Child process pid=%d terminated abnormally: %s\n",
491 : 0 : (int)child_pid, strsignal (WTERMSIG(status)));
492 : 0 : ret = 128 + WTERMSIG(status);
493 : 0 : break;
494 : : }
495 : : }
496 : : }
497 : :
498 : 3452 : exit(ret);
499 : : /* NOTREACHED */
500 : : }
501 : :
502 : : static int
503 : 3459 : expand_aliases(int argc, char ***argv)
504 : : {
505 : 3459 : pkg_iter it = NULL;
506 : : const pkg_object *all_aliases;
507 : : const pkg_object *alias;
508 : : const char *alias_value;
509 : : void *buf;
510 : 3459 : char **oldargv = *argv;
511 : : char **newargv;
512 : : char *args;
513 : : int newargc;
514 : : int spaces;
515 : : int i;
516 : : size_t veclen;
517 : : size_t arglen;
518 : 3459 : bool matched = false;
519 : :
520 : 3459 : all_aliases = pkg_config_get("ALIAS");
521 : :
522 : 86445 : while ((alias = pkg_object_iterate(all_aliases, &it))) {
523 [ + + ]: 83012 : if (strcmp(oldargv[0], pkg_object_key(alias)) == 0) {
524 : 26 : matched = true;
525 : 26 : break;
526 : : }
527 : : }
528 : :
529 : 26 : if (!matched || (alias_value = pkg_object_string(alias)) == NULL)
530 : 3433 : return (argc); /* Nothing to do */
531 : :
532 : : /* Estimate how many args alias_value will split into by
533 : : * counting the number of whitespace characters in it. This
534 : : * will be at minimum one less than the final argc. We'll be
535 : : * consuming one of the orginal argv, so that balances
536 : : * out. */
537 : :
538 : 26 : spaces = pkg_utils_count_spaces(alias_value);
539 : 26 : arglen = strlen(alias_value) + 1;
540 : 26 : veclen = sizeof(char *) * (spaces + argc + 1);
541 : 26 : buf = malloc(veclen + arglen);
542 [ - + ]: 26 : if (buf == NULL)
543 : 0 : err(EXIT_FAILURE, "expanding aliases");
544 : :
545 : 26 : newargv = (char **) buf;
546 : 26 : args = (char *) (buf + veclen);
547 : 26 : strlcpy(args, alias_value, arglen);
548 : :
549 : 26 : newargc = 0;
550 [ + + ]: 78 : while(args != NULL) {
551 : 52 : newargv[newargc++] = pkg_utils_tokenize(&args);
552 : : }
553 [ - + ]: 26 : for (i = 1; i < argc; i++) {
554 : 0 : newargv[newargc++] = oldargv[i];
555 : 0 : }
556 : 26 : newargv[newargc] = NULL;
557 : :
558 : 26 : *argv = newargv;
559 : 26 : return (newargc);
560 : 3459 : }
561 : :
562 : : static
563 : 3452 : bool ptraced(void)
564 : : {
565 : : #if defined(__FreeBSD__)
566 : : int mib[4];
567 : : struct kinfo_proc info;
568 : : size_t size;
569 : :
570 : 3452 : info.ki_flag = 0;
571 : :
572 : 3452 : mib[0] = CTL_KERN;
573 : 3452 : mib[1] = KERN_PROC;
574 : 3452 : mib[2] = KERN_PROC_PID;
575 : 3452 : mib[3] = getpid();
576 : :
577 : 3452 : size = sizeof(info);
578 : 3452 : sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0);
579 : :
580 : 3452 : return ((info.ki_flag & P_TRACED) != 0 );
581 : : #else
582 : : return (false);
583 : : #endif
584 : : }
585 : :
586 : : int
587 : 3494 : main(int argc, char **argv)
588 : : {
589 : : unsigned int i;
590 : 3494 : struct commands *command = NULL;
591 : 3494 : unsigned int ambiguous = 0;
592 : 3494 : const char *chroot_path = NULL;
593 : 3494 : const char *rootdir = NULL;
594 : : #ifdef HAVE_LIBJAIL
595 : : int jid;
596 : : #endif
597 : 3494 : const char *jail_str = NULL;
598 : : size_t len;
599 : : signed char ch;
600 : 3494 : int64_t debug = 0;
601 : 3494 : int version = 0;
602 : 3494 : int ret = EXIT_SUCCESS;
603 : 3494 : bool plugins_enabled = false;
604 : 3494 : bool plugin_found = false;
605 : 3494 : bool show_commands = false;
606 : 3494 : bool activation_test = false;
607 : 3494 : pkg_init_flags init_flags = 0;
608 : : struct plugcmd *c;
609 : 3494 : const char *conffile = NULL;
610 : 3494 : const char *reposdir = NULL;
611 : : char **save_argv;
612 : : char realrootdir[MAXPATHLEN];
613 : : int j;
614 : :
615 : 3494 : struct option longopts[] = {
616 : : { "debug", no_argument, NULL, 'd' },
617 : : #ifdef HAVE_LIBJAIL
618 : : { "jail", required_argument, NULL, 'j' },
619 : : #endif
620 : : { "chroot", required_argument, NULL, 'c' },
621 : : { "config", required_argument, NULL, 'C' },
622 : : { "repo-conf-dir", required_argument, NULL, 'R' },
623 : : { "rootdir", required_argument, NULL, 'r' },
624 : : { "list", no_argument, NULL, 'l' },
625 : : { "version", no_argument, NULL, 'v' },
626 : : { "option", required_argument, NULL, 'o' },
627 : : { "only-ipv4", no_argument, NULL, '4' },
628 : : { "only-ipv6", no_argument, NULL, '6' },
629 : : { NULL, 0, NULL, 0 },
630 : : };
631 : :
632 : : /* Set stdout unbuffered */
633 : 3494 : setvbuf(stdout, NULL, _IONBF, 0);
634 : :
635 : : /* Ignore SIGPIPE */
636 : 3494 : signal(SIGPIPE, SIG_IGN);
637 : :
638 : 3494 : if (argc < 2)
639 : 0 : usage(NULL, NULL, stderr, PKG_USAGE_INVALID_ARGUMENTS, "not enough arguments");
640 : :
641 : : /* getopt_long() will permute the arg-list unless
642 : : * POSIXLY_CORRECT is set in the environment. This is a
643 : : * difference to the original getopt() we were using, and
644 : : * screws up our 'pkg {pkg-opts} verb {verb-opts}' command
645 : : * line concept. */
646 : :
647 [ - + ]: 3494 : if (setenv("POSIXLY_CORRECT", "1", 1) == -1)
648 : 0 : err(EXIT_FAILURE, "setenv() failed");
649 : :
650 : 3494 : save_argv = argv;
651 : :
652 : : #ifdef HAVE_LIBJAIL
653 : : #define JAIL_OPT "j:"
654 : : #else
655 : : #define JAIL_OPT
656 : : #endif
657 [ + + ]: 5133 : while ((ch = getopt_long(argc, argv, "+d"JAIL_OPT"c:C:R:r:lNvo:46", longopts, NULL)) != -1) {
658 [ + - + + : 1639 : switch (ch) {
+ - - + +
+ - - - ]
659 : : case 'd':
660 : 16 : debug++;
661 : 16 : break;
662 : : case 'c':
663 : 0 : chroot_path = optarg;
664 : 0 : break;
665 : : case 'C':
666 : 414 : conffile = optarg;
667 : 414 : break;
668 : : case 'R':
669 : 21 : reposdir = optarg;
670 : 21 : break;
671 : : case 'r':
672 : 302 : rootdir = optarg;
673 : 302 : break;
674 : : #ifdef HAVE_LIBJAIL
675 : : case 'j':
676 : 0 : jail_str = optarg;
677 : 0 : break;
678 : : #endif
679 : : case 'l':
680 : 0 : show_commands = true;
681 : 0 : break;
682 : : case 'N':
683 : 13 : activation_test = true;
684 : 13 : break;
685 : : case 'v':
686 : 68 : version++;
687 : 68 : break;
688 : : case 'o':
689 : 805 : export_arg_option (optarg);
690 : 805 : break;
691 : : case '4':
692 : 0 : init_flags = PKG_INIT_FLAG_USE_IPV4;
693 : 0 : break;
694 : : case '6':
695 : 0 : init_flags = PKG_INIT_FLAG_USE_IPV6;
696 : 0 : break;
697 : : default:
698 : 0 : errx(EXIT_FAILURE, "Invalid argument provided");
699 : : break;
700 : : }
701 : : }
702 : 3494 : argc -= optind;
703 : 3494 : argv += optind;
704 : :
705 : 3494 : pkg_set_debug_level(debug);
706 : :
707 : 3494 : if (version == 1)
708 : 0 : show_version_info(version);
709 : :
710 : 0 : if (show_commands && version == 0) {
711 : 0 : show_command_names();
712 : 0 : exit(EXIT_SUCCESS);
713 : : }
714 : :
715 : 47 : if (argc == 0 && version == 0 && !activation_test)
716 : 0 : usage(conffile, reposdir, stderr, PKG_USAGE_INVALID_ARGUMENTS, "no commands specified");
717 : :
718 : 3486 : umask(022);
719 : 3486 : pkg_event_register(&event_callback, &debug);
720 : :
721 : : /* reset getopt for the next call */
722 : 3486 : optreset = 1;
723 : 3486 : optind = 1;
724 : :
725 : 3486 : if (debug == 0 && version == 0 && !ptraced())
726 : 3452 : start_process_worker(save_argv);
727 : :
728 : : #ifdef HAVE_ARC4RANDOM_STIR
729 : : /* Ensure that random is stirred after a possible fork */
730 : : arc4random_stir();
731 : : #endif
732 : :
733 : 7208 : if ((jail_str != NULL && (chroot_path != NULL || rootdir != NULL)) ||
734 : 0 : (chroot_path != NULL && (jail_str != NULL || rootdir != NULL)) ||
735 : 304 : (rootdir != NULL && (jail_str != NULL || chroot_path != NULL))) {
736 : 0 : usage(conffile, reposdir, stderr, PKG_USAGE_INVALID_ARGUMENTS,
737 : : "-j, -c and/or -r cannot be used at the same time!\n");
738 : 0 : }
739 : :
740 : 0 : if (chroot_path != NULL) {
741 [ # # ]: 0 : if (chroot(chroot_path) == -1) {
742 : 0 : err(EXIT_FAILURE, "chroot failed");
743 : : }
744 : 0 : }
745 : :
746 : : #ifdef HAVE_LIBJAIL
747 : 0 : if (jail_str != NULL) {
748 : 0 : jid = jail_getid(jail_str);
749 [ # # ]: 0 : if (jid < 0)
750 : 0 : errx(1, "%s", jail_errmsg);
751 : :
752 [ # # ]: 0 : if (jail_attach(jid) == -1)
753 : 0 : err(1, "jail_attach(%s)", jail_str);
754 : 0 : }
755 : :
756 : 3506 : if (jail_str != NULL || chroot_path != NULL)
757 [ # # ]: 0 : if (chdir("/") == -1)
758 : 0 : errx(EXIT_FAILURE, "chdir() failed");
759 : : #endif
760 : :
761 : 304 : if (rootdir != NULL) {
762 [ - + ]: 304 : if (realpath(rootdir, realrootdir) == NULL)
763 : 0 : err(EXIT_FAILURE, "Invalid rootdir");
764 [ - + ]: 304 : if (chdir(rootdir) == -1)
765 : 0 : errx(EXIT_FAILURE, "chdir() failed");
766 [ - + ]: 304 : if (pkg_set_rootdir(realrootdir) != EPKG_OK)
767 : 0 : exit(EXIT_FAILURE);
768 : 304 : }
769 : :
770 [ - + ]: 3506 : if (pkg_ini(conffile, reposdir, init_flags) != EPKG_OK)
771 : 0 : errx(EXIT_FAILURE, "Cannot parse configuration file!");
772 : :
773 : 3506 : if (debug > 0)
774 : 8 : pkg_set_debug_level(debug);
775 : :
776 [ - + ]: 3506 : if (atexit(&pkg_shutdown) != 0)
777 : 0 : errx(EXIT_FAILURE, "register pkg_shutdown() to run at exit");
778 : :
779 : 3506 : if (jail_str == NULL && !pkg_compiled_for_same_os_major())
780 : 0 : warnx("Warning: Major OS version upgrade detected. Running "
781 : : "\"pkg bootstrap -f\" recommended");
782 : :
783 : :
784 : 26 : plugins_enabled = pkg_object_bool(pkg_config_get("PKG_ENABLE_PLUGINS"));
785 : :
786 : 26 : if (plugins_enabled) {
787 : 26 : struct pkg_plugin *p = NULL;
788 : :
789 [ - + ]: 26 : if (pkg_plugins_init() != EPKG_OK)
790 : 0 : errx(EXIT_FAILURE, "Plugins cannot be loaded");
791 : :
792 [ - + ]: 26 : if (atexit(&pkg_plugins_shutdown) != 0)
793 : 0 : errx(EXIT_FAILURE,
794 : : "register pkg_plugins_shutdown() to run at exit");
795 : :
796 : : /* load commands plugins */
797 [ - + ]: 26 : while (pkg_plugins(&p) != EPKG_END) {
798 : : int n;
799 : :
800 : 0 : nb_cmd *ncmd = pkg_plugin_func(p, "pkg_register_cmd_count");
801 : 0 : register_cmd *reg = pkg_plugin_func(p, "pkg_register_cmd");
802 : 0 : if (reg != NULL && ncmd != NULL) {
803 : 0 : n = ncmd();
804 [ # # ]: 0 : for (j = 0; j < n ; j++) {
805 : 0 : c = malloc(sizeof(struct plugcmd));
806 : 0 : reg(j, &c->name, &c->desc, &c->exec);
807 [ # # ]: 0 : DL_APPEND(plugins, c);
808 : 0 : }
809 : 0 : }
810 : : }
811 : 26 : }
812 : :
813 : 34 : if (version > 1)
814 : 34 : show_version_info(version);
815 : :
816 : 55 : if (activation_test)
817 : 13 : do_activation_test(argc);
818 : :
819 : 3459 : if (argc >= 1 && strcmp(argv[0], "bootstrap") == 0) {
820 [ # # ]: 0 : if (argc == 1) {
821 : 0 : printf("pkg(8) already installed, use -f to force.\n");
822 : 0 : exit(EXIT_SUCCESS);
823 : 0 : } else if (argc == 2 && strcmp(argv[1], "-f") == 0) {
824 [ # # ]: 0 : if (access("/usr/sbin/pkg", R_OK) == 0) {
825 : : /* Only 10.0+ supported 'bootstrap -f' */
826 : : #if __FreeBSD_version < 1000502
827 : : printf("Execute these steps to rebootstrap"
828 : : " pkg(8):\n");
829 : : printf("# pkg delete -f pkg\n");
830 : : printf("# /usr/sbin/pkg -v\n");
831 : : exit(EXIT_SUCCESS);
832 : : #endif
833 : 0 : printf("pkg(8) is already installed. Forcing "
834 : : "reinstallation through pkg(7).\n");
835 : 0 : execl("/usr/sbin/pkg", "pkg", "bootstrap",
836 : : "-f", NULL);
837 : : /* NOTREACHED */
838 : 0 : } else
839 : 0 : errx(EXIT_FAILURE, "pkg(7) bootstrapper not"
840 : : " found at /usr/sbin/pkg.");
841 : 0 : }
842 : 0 : }
843 : :
844 : 3459 : save_argv = argv;
845 : 3459 : argc = expand_aliases(argc, &argv);
846 : :
847 : 3459 : len = strlen(argv[0]);
848 : 49592 : for (i = 0; i < cmd_len; i++) {
849 : 49592 : if (strncmp(argv[0], cmd[i].name, len) == 0) {
850 : : /* if we have the exact cmd */
851 [ + - ]: 3459 : if (len == strlen(cmd[i].name)) {
852 : 3459 : command = &cmd[i];
853 : 3459 : ambiguous = 0;
854 : 3459 : break;
855 : : }
856 : :
857 : : /*
858 : : * we already found a partial match so `argv[0]' is
859 : : * an ambiguous shortcut
860 : : */
861 : 0 : ambiguous++;
862 : :
863 : 0 : command = &cmd[i];
864 : 0 : }
865 : 46133 : }
866 : :
867 : 3459 : set_globals();
868 : :
869 [ - + ]: 3459 : if (command == NULL) {
870 : : /* Check if a plugin provides the requested command */
871 : 0 : ret = EPKG_FATAL;
872 : 0 : if (plugins_enabled) {
873 : 0 : DL_FOREACH(plugins, c) {
874 [ # # ]: 0 : if (strcmp(c->name, argv[0]) == 0) {
875 : 0 : plugin_found = true;
876 : 0 : ret = c->exec(argc, argv);
877 : 0 : break;
878 : : }
879 : 0 : }
880 : 0 : }
881 : :
882 : 0 : if (!plugin_found)
883 : 0 : usage(conffile, reposdir, stderr, PKG_USAGE_UNKNOWN_COMMAND, argv[0]);
884 : :
885 : 0 : return (ret);
886 : : }
887 : :
888 [ + - ]: 3459 : if (ambiguous <= 1) {
889 [ + - ]: 3459 : assert(command->exec != NULL);
890 : 3459 : ret = command->exec(argc, argv);
891 : 3459 : } else {
892 : 0 : usage(conffile, reposdir, stderr, PKG_USAGE_UNKNOWN_COMMAND, argv[0]);
893 : : }
894 : :
895 : 3459 : if (save_argv != argv)
896 : 26 : free(argv);
897 : :
898 : 3204 : if (ret == EXIT_SUCCESS && newpkgversion)
899 : 13 : return (EX_NEEDRESTART);
900 : :
901 : 3446 : return (ret);
902 : 3459 : }
903 : :
|