From 82430d8410cdee0814639155d5e50952bbabf0e9 Mon Sep 17 00:00:00 2001 From: Ricardo Branco Date: Thu, 30 May 2024 16:12:29 +0200 Subject: [PATCH] Add ps command to list processes using deleted binaries and libraries --- auto.def | 7 ++ docs/Makefile.autosetup | 1 + docs/pkg-ps.8 | 83 ++++++++++++++++++++ src/Makefile.autosetup | 4 + src/main.c | 3 + src/pkgcli.h | 4 + src/ps.c | 169 ++++++++++++++++++++++++++++++++++++++++ 7 files changed, 271 insertions(+) create mode 100644 docs/pkg-ps.8 create mode 100644 src/ps.c diff --git a/auto.def b/auto.def index 9d0505d73..1ff79bf24 100644 --- a/auto.def +++ b/auto.def @@ -106,6 +106,13 @@ if {[string match *-freebsd* [get-define host]]} { define-feature libmd } } + cc-with { -libs { -lprocstat }} { + if {![cc-check-functions procstat_open_sysctl]} { + user-error "Unable to find libprocstat" + } else { + define-feature libprocstat + } + } } cc-with { -libs { -larchive }} { diff --git a/docs/Makefile.autosetup b/docs/Makefile.autosetup index e929d144b..ecab34f3e 100644 --- a/docs/Makefile.autosetup +++ b/docs/Makefile.autosetup @@ -26,6 +26,7 @@ MAN8= pkg-add.8 \ pkg-key.8 \ pkg-lock.8 \ pkg-query.8 \ + pkg-ps.8 \ pkg-register.8 \ pkg-repo.8 \ pkg-rquery.8 \ diff --git a/docs/pkg-ps.8 b/docs/pkg-ps.8 new file mode 100644 index 000000000..af43823ad --- /dev/null +++ b/docs/pkg-ps.8 @@ -0,0 +1,83 @@ +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" +.\" @(#)pkg.8 +.\" +.Dd May 30, 2024 +.Dt PKG-PS 8 +.Os +.Sh NAME +.Nm "pkg ps" +.Nd list running processes using deleted binaries and libraries +.Sh SYNOPSIS +.Nm +.Op Fl v +.Pp +.Nm +.Op Cm --verbose +.Sh DESCRIPTION +.Nm +List running processes using deleted binaries and libraries. +This program must be run as root if any of these sysctl's are set to zero: +.Bl -column +.It "security.bsd.unprivileged_proc_debug" +.It "security.bsd.see_other_gids" +.It "security.bsd.see_other_uids" +.El +.Sh OPTIONS +The following options are supported by +.Nm : +.Bl -tag -width xxxxxxxxxxxx +.It Fl v , Cm --verbose +Verbose output. List full command line arguments. +.El +.Sh FILES +See +.Xr pkg.conf 5 . +.Sh SEE ALSO +.Xr pkg_create 3 , +.Xr pkg_printf 3 , +.Xr pkg_repos 3 , +.Xr pkg-keywords 5 , +.Xr pkg-lua-script 5 , +.Xr pkg-repository 5 , +.Xr pkg-script 5 , +.Xr pkg.conf 5 , +.Xr pkg 8 , +.Xr pkg-add 8 , +.Xr pkg-alias 8 , +.Xr pkg-annotate 8 , +.Xr pkg-audit 8 , +.Xr pkg-autoremove 8 , +.Xr pkg-check 8 , +.Xr pkg-clean 8 , +.Xr pkg-config 8 , +.Xr pkg-create 8 , +.Xr pkg-delete 8 , +.Xr pkg-fetch 8 , +.Xr pkg-info 8 , +.Xr pkg-install 8 , +.Xr pkg-lock 8 , +.Xr pkg-query 8 , +.Xr pkg-register 8 , +.Xr pkg-repo 8 , +.Xr pkg-rquery 8 , +.Xr pkg-search 8 , +.Xr pkg-set 8 , +.Xr pkg-shell 8 , +.Xr pkg-shlib 8 , +.Xr pkg-ssh 8 , +.Xr pkg-stats 8 , +.Xr pkg-update 8 , +.Xr pkg-updating 8 , +.Xr pkg-upgrade 8 , +.Xr pkg-version 8 , +.Xr pkg-which 8 diff --git a/src/Makefile.autosetup b/src/Makefile.autosetup index f7668a647..608a0bb03 100644 --- a/src/Makefile.autosetup +++ b/src/Makefile.autosetup @@ -70,6 +70,10 @@ OTHER_LIBS+= -lresolv @if HAVE_LIBMD OTHER_LIBS+= -lmd @endif +@if HAVE_LIBPROCSTAT +SRCS+= ps.c +OTHER_LIBS+= -lprocstat -lkvm +@endif @if libmachista LOCAL_LDFLAGS= $(LIBPKGFLAT) $(LIBS) $(OTHER_LIBS) -lresolv diff --git a/src/main.c b/src/main.c index 68e9cb1b2..a9e2deab6 100644 --- a/src/main.c +++ b/src/main.c @@ -96,6 +96,9 @@ static struct commands { { "install", "Installs packages from remote package repositories and local archives", exec_install, usage_install}, { "lock", "Locks package against modifications or deletion", exec_lock, usage_lock}, { "plugins", "Manages plugins and displays information about plugins", exec_plugins, usage_plugins}, +#ifdef HAVE_LIBPROCSTAT + { "ps", "List running processes using deleted binaries and libraries", exec_ps, usage_ps}, +#endif { "query", "Queries information about installed packages", exec_query, usage_query}, { "register", "Registers a package into the local database", exec_register, usage_register}, { "remove", "Deletes packages from the database and the system", exec_delete, usage_delete}, diff --git a/src/pkgcli.h b/src/pkgcli.h index f9da824e5..903ad08ef 100644 --- a/src/pkgcli.h +++ b/src/pkgcli.h @@ -102,6 +102,10 @@ int exec_lock(int, char **); int exec_unlock(int, char **); void usage_lock(void); +/* pkg ps */ +int exec_ps(int, char **); +void usage_ps(void); + /* pkg query */ int exec_query(int, char **); void usage_query(void); diff --git a/src/ps.c b/src/ps.c new file mode 100644 index 000000000..3e12aed04 --- /dev/null +++ b/src/ps.c @@ -0,0 +1,169 @@ +/*- + * Copyright (c) 2024 Ricardo Branco + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer + * in this position and unchanged. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "pkgcli.h" + +static char *safe_string(char *); +static void print_argv(struct procstat *, struct kinfo_proc *); +static void print_proc(struct procstat *, struct kinfo_proc *, int); + +void +usage_ps(void) +{ + fprintf(stderr, + "Usage: pkg ps [-v]\n\n"); + fprintf(stderr, "For more information see 'pkg help install'.\n"); +} + +int +exec_ps(int argc, char **argv) +{ + struct kinfo_proc *procs; + struct procstat *ps; + int ch; + unsigned int count; + bool verbose = false; + + struct option longopts[] = { + { "verbose", no_argument, NULL, 'v' }, + { NULL, 0, NULL, 0 }, + }; + + while ((ch = getopt_long(argc, argv, "v", longopts, NULL)) != -1) { + switch (ch) { + case 'v': + verbose = true; + break; + default: + usage_install(); + return (EXIT_FAILURE); + } + } + argc -= optind; + argv += optind; + + if (argc > 0) { + usage_ps(); + return (EXIT_FAILURE); + } + + /* Will fail if security.bsd.unprivileged_proc_debug=0 */ + ps = procstat_open_sysctl(); + if (ps == NULL) + err(1, "procstat_open_sysctl"); + + procs = procstat_getprocs(ps, KERN_PROC_PROC, 0, &count); + if (procs == NULL) { + procstat_close(ps); + err(1, "procstat_getprocs"); + } + + printf("PID\tPPID\tUID\tUser\tCommand\n"); + for (unsigned int i = 0; i < count; i++) + if (procs[i].ki_pid != 0) + print_proc(ps, &procs[i], verbose); + + procstat_freeprocs(ps, procs); + procstat_close(ps); + return (0); +} + +static void +print_proc(struct procstat *ps, struct kinfo_proc *kp, int verbose) +{ + unsigned int count; + + struct kinfo_vmentry *vmmap = procstat_getvmmap(ps, kp, &count); + if (vmmap == NULL) { + if (errno != EPERM && errno != ENOENT) + err(1, "procstat_getvmmap: %d", kp->ki_pid); + return; + } + + for (unsigned int i = 0; i < count; i++) + /* Print executable mappings with no path */ + if (vmmap[i].kve_type == KVME_TYPE_VNODE && + vmmap[i].kve_protection & KVME_PROT_EXEC && + vmmap[i].kve_path[0] == '\0') { + printf("%d\t%d\t%d\t%s\t%s\n", kp->ki_pid, kp->ki_ppid, + kp->ki_ruid, kp->ki_login, kp->ki_comm); + if (verbose) + print_argv(ps, kp); + break; + } + + procstat_freevmmap(ps, vmmap); +} + +static void +print_argv(struct procstat *ps, struct kinfo_proc *kp) +{ + char **argv = procstat_getargv(ps, kp, 0); + if (argv == NULL) { + warn("procstat_getargv: %d", kp->ki_pid); + return; + } + + printf("\t"); + do { + printf(" %s", safe_string(*argv)); + } while (*++argv); + printf("\n"); + + procstat_freeargv(ps); +} + +static char * +safe_string(char *arg) { + static char *vis = NULL; + + if (vis == NULL) { + vis = malloc(PATH_MAX * 4 + 1); + if (vis == NULL) + err(1, "malloc"); + } + (void)strvis(vis, arg, VIS_TAB | VIS_NL | VIS_CSTYLE); + + return vis; +}