summaryrefslogtreecommitdiffstats
path: root/src/fuser.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 13:17:26 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 13:17:26 +0000
commita4dc4fbcc1b131061dcccd5cdcc2395d9a2ba784 (patch)
treebc531e7e083fff1668bb4e79e9692d8cc47aea95 /src/fuser.c
parentInitial commit. (diff)
downloadpsmisc-a4dc4fbcc1b131061dcccd5cdcc2395d9a2ba784.tar.xz
psmisc-a4dc4fbcc1b131061dcccd5cdcc2395d9a2ba784.zip
Adding upstream version 23.7.upstream/23.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/fuser.c')
-rw-r--r--src/fuser.c2237
1 files changed, 2237 insertions, 0 deletions
diff --git a/src/fuser.c b/src/fuser.c
new file mode 100644
index 0000000..0c14128
--- /dev/null
+++ b/src/fuser.c
@@ -0,0 +1,2237 @@
+/*
+ * fuser.c - identify processes using files
+ *
+ * Based on fuser.c Copyright (C) 1993-2005 Werner Almesberger and Craig Small
+ *
+ * Completely re-written
+ * Copyright (C) 2005-2024 Craig Small <csmall@dropbear.xyz>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/sysmacros.h>
+#include <sys/wait.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <pwd.h>
+#include <netdb.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <mntent.h>
+#include <signal.h>
+#include <getopt.h>
+#include <setjmp.h>
+#include <limits.h>
+/* MAXSYMLINKS is a BSDism. If it doesn't exist, fall back to SYMLINK_MAX,
+ which is the POSIX name. */
+#ifndef MAXSYMLINKS
+#define MAXSYMLINKS SYMLINK_MAX
+#endif
+
+#ifdef ENABLE_NLS
+#include <locale.h>
+#endif
+
+#include "fuser.h"
+#include "signals.h"
+#include "i18n.h"
+#include "statx.h"
+#include "comm.h"
+
+//#define DEBUG 1
+
+#ifndef PATH_MAX
+#define PATH_MAX 4096
+#endif /* PATH_MAX */
+
+#define NAME_FIELD 20 /* space reserved for file name */
+/* Function defines */
+static void add_matched_proc(struct names *name_list, const pid_t pid,
+ const uid_t uid, const char access);
+static void add_special_proc(struct names *name_list, const char ptype,
+ const uid_t uid, const char *command);
+static void check_dir(const pid_t pid, const char *dirname,
+ struct device_list *dev_head,
+ struct inode_list *ino_head, const uid_t uid,
+ const char access, struct unixsocket_list *sockets,
+ dev_t netdev);
+static void check_map(const pid_t pid, const char *filename,
+ struct device_list *dev_head,
+ struct inode_list *ino_head, const uid_t uid,
+ const char access);
+static struct stat *get_pidstat(const pid_t pid, const char *filename);
+static uid_t getpiduid(const pid_t pid);
+static int print_matches(struct names *names_head, const opt_type opts,
+ const int sig_number);
+static int kill_matched_proc(struct procs *pptr, const opt_type opts,
+ const int sig_number);
+
+/*int parse_mount(struct names *this_name, struct device_list **dev_list);*/
+static void add_device(struct device_list **dev_list,
+ struct names *this_name, dev_t device);
+void fill_unix_cache(struct unixsocket_list **unixsocket_head);
+void clear_unix_cache(struct unixsocket_list **unixsocket_head);
+static void atexit_clear_unix_cache();
+static dev_t find_net_dev(void);
+static void scan_procs(
+ struct names *names_head, struct inode_list *ino_head,
+ struct device_list *dev_head,
+ struct unixsocket_list *sockets, dev_t netdev);
+static void scan_knfsd(
+ struct names *names_head, struct inode_list *ino_head,
+ struct device_list *dev_head);
+static void scan_mounts(
+ struct names *names_head,
+ struct inode_list *ino_head,
+ struct device_list *dev_head);
+static void scan_swaps(
+ struct names *names_head,
+ struct inode_list *ino_head,
+ struct device_list *dev_head);
+#ifdef DEBUG
+static void debug_match_lists(
+ struct names *names_head,
+ struct inode_list *ino_head,
+ struct device_list *dev_head);
+#endif
+
+static char *expandpath(const char *path);
+static struct unixsocket_list *unixsockets = NULL;
+static struct names *names_head = NULL, *names_tail = NULL;
+static struct ip_connections *tcp_connection_list = NULL;
+static struct ip_connections *udp_connection_list = NULL;
+#ifdef WITH_IPV6
+static struct ip6_connections *tcp6_connection_list = NULL;
+static struct ip6_connections *udp6_connection_list = NULL;
+#endif
+static struct device_list *match_devices = NULL;
+static struct inode_list *match_inodes = NULL;
+
+static void usage(
+ const char *errormsg)
+{
+ if (errormsg != NULL)
+ fprintf(stderr, "%s\n", errormsg);
+
+ fprintf(stderr,
+ _
+ ("Usage: fuser [-fIMuvw] [-a|-s] [-4|-6] [-c|-m|-n SPACE]\n"
+ " [-k [-i] [-SIGNAL]] NAME...\n"
+ " fuser -l\n"
+ " fuser -V\n"
+ "Show which processes use the named files, sockets, or filesystems.\n\n"
+ " -a,--all display unused files too\n"
+ " -i,--interactive ask before killing (ignored without -k)\n"
+ " -I,--inode use always inodes to compare files\n"
+ " -k,--kill kill processes accessing the named file\n"
+ " -l,--list-signals list available signal names\n"
+ " -m,--mount show all processes using the named filesystems or\n"
+ " block device\n"
+ " -M,--ismountpoint fulfill request only if NAME is a mount point\n"
+ " -n,--namespace SPACE search in this name space (file, udp, or tcp)\n"
+ " -s,--silent silent operation\n"
+ " -SIGNAL send this signal instead of SIGKILL\n"
+ " -u,--user display user IDs\n"
+ " -v,--verbose verbose output\n"
+ " -w,--writeonly kill only processes with write access\n"
+ " -V,--version display version information\n"));
+#ifdef WITH_IPV6
+ fprintf(stderr, _(" -4,--ipv4 search IPv4 sockets only\n"
+ " -6,--ipv6 search IPv6 sockets only\n"));
+#endif
+ fprintf(stderr, _(" udp/tcp names: [local_port][,[rmt_host][,[rmt_port]]]\n\n"));
+ exit(1);
+}
+
+void print_version()
+{
+ fprintf(stderr, _("fuser (PSmisc) %s\n"), VERSION);
+ fprintf(stderr,
+ _("Copyright (C) 1993-2024 Werner Almesberger and Craig Small\n\n"));
+ fprintf(stderr,
+ _("PSmisc comes with ABSOLUTELY NO WARRANTY.\n"
+ "This is free software, and you are welcome to redistribute it under\n"
+ "the terms of the GNU General Public License.\n"
+ "For more information about these matters, see the files named COPYING.\n"));
+}
+
+static void scan_procs(
+ struct names *names_head, struct inode_list *ino_head,
+ struct device_list *dev_head, struct unixsocket_list *sockets,
+ dev_t netdev)
+{
+ DIR *topproc_dir;
+ struct dirent *topproc_dent;
+ struct inode_list *ino_tmp;
+ struct device_list *dev_tmp;
+ pid_t pid, my_pid;
+ uid_t uid;
+
+ if ( (ino_head == NULL) && (dev_head == NULL) )
+ return;
+
+ if ((topproc_dir = opendir("/proc")) == NULL) {
+ fprintf(stderr, _("Cannot open /proc directory: %s\n"),
+ strerror(errno));
+ exit(1);
+ }
+ my_pid = getpid();
+ while ((topproc_dent = readdir(topproc_dir)) != NULL) {
+ dev_t cwd_dev, exe_dev, root_dev;
+ struct stat *cwd_stat = NULL;
+ struct stat *exe_stat = NULL;
+ struct stat *root_stat = NULL;
+
+ if (topproc_dent->d_name[0] < '0' || topproc_dent->d_name[0] > '9') /* Not a process */
+ continue;
+ pid = atoi(topproc_dent->d_name);
+ /* Dont print myself */
+ if (pid == my_pid)
+ continue;
+ uid = getpiduid(pid);
+
+ cwd_stat = get_pidstat(pid, "cwd");
+ exe_stat = get_pidstat(pid, "exe");
+ root_stat = get_pidstat(pid, "root");
+ cwd_dev = cwd_stat ? cwd_stat->st_dev : 0;
+ exe_dev = exe_stat ? exe_stat->st_dev : 0;
+ root_dev = root_stat ? root_stat->st_dev : 0;
+
+ /* Scan the devices */
+ for (dev_tmp = dev_head; dev_tmp != NULL;
+ dev_tmp = dev_tmp->next) {
+ if (exe_dev == dev_tmp->device)
+ add_matched_proc(dev_tmp->name, pid, uid,
+ ACCESS_EXE);
+ if (root_dev == dev_tmp->device)
+ add_matched_proc(dev_tmp->name, pid, uid,
+ ACCESS_ROOT);
+ if (cwd_dev == dev_tmp->device)
+ add_matched_proc(dev_tmp->name, pid, uid,
+ ACCESS_CWD);
+ }
+ for (ino_tmp = ino_head; ino_tmp != NULL;
+ ino_tmp = ino_tmp->next) {
+ if (exe_dev == ino_tmp->device) {
+ if (!exe_stat)
+ exe_stat = get_pidstat(pid, "exe");
+ if (exe_stat
+ && exe_stat->st_dev == ino_tmp->device
+ && exe_stat->st_ino == ino_tmp->inode)
+ add_matched_proc(ino_tmp->name, pid,
+ uid, ACCESS_EXE);
+ }
+ if (root_dev == ino_tmp->device) {
+ if (!root_stat)
+ root_stat = get_pidstat(pid, "root");
+ if (root_stat
+ && root_stat->st_dev == ino_tmp->device
+ && root_stat->st_ino == ino_tmp->inode)
+ add_matched_proc(ino_tmp->name, pid,
+ uid, ACCESS_ROOT);
+ }
+ if (cwd_dev == ino_tmp->device) {
+ if (!cwd_stat)
+ cwd_stat = get_pidstat(pid, "cwd");
+ if (cwd_stat
+ && cwd_stat->st_dev == ino_tmp->device
+ && cwd_stat->st_ino == ino_tmp->inode)
+ add_matched_proc(ino_tmp->name, pid,
+ uid, ACCESS_CWD);
+ }
+ }
+ if (root_stat)
+ free(root_stat);
+ if (cwd_stat)
+ free(cwd_stat);
+ if (exe_stat)
+ free(exe_stat);
+#if !defined (__linux__) && !defined (__CYGWIN__)
+ check_dir(pid, "lib", dev_head, ino_head, uid, ACCESS_MMAP,
+ sockets, netdev);
+ check_dir(pid, "mmap", dev_head, ino_head, uid, ACCESS_MMAP,
+ sockets, netdev);
+#endif
+ check_dir(pid, "fd", dev_head, ino_head, uid, ACCESS_FILE,
+ sockets, netdev);
+ check_map(pid, "maps", dev_head, ino_head, uid, ACCESS_MMAP);
+
+ } /* while topproc_dent */
+ closedir(topproc_dir);
+}
+
+static void add_inode(
+ struct inode_list **ino_list,
+ struct names *this_name,
+ dev_t device, ino_t inode)
+{
+ struct inode_list *ino_tmp, *ino_head;
+
+ if ((ino_tmp =
+ (struct inode_list *)malloc(sizeof(struct inode_list))) == NULL)
+ return;
+ ino_head = *ino_list;
+ ino_tmp->name = this_name;
+ ino_tmp->device = device;
+ ino_tmp->inode = inode;
+ ino_tmp->next = ino_head;
+ *ino_list = ino_tmp;
+}
+
+static void add_device(
+ struct device_list **dev_list,
+ struct names *this_name,
+ dev_t device)
+{
+ struct device_list *dev_tmp, *dev_head;
+#ifdef DEBUG
+ fprintf(stderr, "add_device(%s %u\n", this_name->filename,
+ (unsigned int)device);
+#endif /* DEBUG */
+
+ if ((dev_tmp =
+ (struct device_list *)malloc(sizeof(struct device_list))) == NULL)
+ return;
+ dev_head = *dev_list;
+ dev_tmp->name = this_name;
+ dev_tmp->device = device;
+ dev_tmp->next = dev_head;
+ *dev_list = dev_tmp;
+}
+
+static void add_ip_conn(
+ struct ip_connections **ip_list, const char *protocol,
+ struct names *this_name, const int lcl_port, const int rmt_port,
+ unsigned long rmt_address)
+{
+ struct ip_connections *ip_tmp, *ip_head;
+
+ if ((ip_tmp =
+ (struct ip_connections *)malloc(sizeof(struct ip_connections))) ==
+ NULL)
+ return;
+ ip_head = *ip_list;
+ ip_tmp->name = this_name;
+ ip_tmp->lcl_port = lcl_port;
+ ip_tmp->rmt_port = rmt_port;
+ ip_tmp->rmt_address.s_addr = rmt_address;
+ ip_tmp->next = ip_head;
+
+ *ip_list = ip_tmp;
+}
+
+#ifdef WITH_IPV6
+static void add_ip6_conn(
+ struct ip6_connections **ip_list,
+ const char *protocol,
+ struct names *this_name,
+ const int lcl_port,
+ const int rmt_port,
+ struct in6_addr rmt_address)
+{
+ struct ip6_connections *ip_tmp, *ip_head;
+
+ if ((ip_tmp =
+ (struct ip6_connections *)malloc(sizeof(struct ip6_connections)))
+ == NULL)
+ return;
+ ip_head = *ip_list;
+ ip_tmp->name = this_name;
+ ip_tmp->lcl_port = lcl_port;
+ ip_tmp->rmt_port = rmt_port;
+ memcpy(&(ip_tmp->rmt_address), &(rmt_address), sizeof(struct in6_addr));
+ ip_tmp->next = ip_head;
+
+ *ip_list = ip_tmp;
+}
+#endif
+
+/* Adds a normal process only */
+static void add_matched_proc(
+ struct names *name_list,
+ const pid_t pid,
+ const uid_t uid,
+ const char access)
+{
+ struct procs *pptr, *last_proc;
+ char *pathname;
+ char cmdname[101], *cptr;
+ int cmdlen;
+ FILE *fp;
+
+ last_proc = NULL;
+ for (pptr = name_list->matched_procs; pptr != NULL; pptr = pptr->next)
+ {
+ last_proc = pptr;
+ if (pptr->pid == pid)
+ {
+ pptr->access |= access;
+ return;
+ }
+ }
+ /* Not found */
+ if ((pptr = (struct procs *)malloc(sizeof(struct procs))) == NULL)
+ {
+ fprintf(stderr,
+ _("Cannot allocate memory for matched proc: %s\n"),
+ strerror(errno));
+ return;
+ }
+ pptr->pid = pid;
+ pptr->uid = uid;
+ pptr->access = access;
+ pptr->proc_type = PTYPE_NORMAL;
+ pptr->next = NULL;
+ /* set command name */
+ pptr->command = NULL;
+
+ fp = NULL;
+ pathname = NULL;
+ if ((asprintf(&pathname, "/proc/%d/stat", pid) > 0) &&
+ ((fp = fopen(pathname, "r")) != NULL) &&
+ (fscanf(fp, "%*d (%100[^)]", cmdname) == 1))
+ if ((pptr->command = (char *)malloc(COMM_LEN + 1)) != NULL)
+ {
+ cmdlen = 0;
+ for (cptr = cmdname; cmdlen < COMM_LEN && *cptr;
+ cptr++)
+ {
+ if (isprint(*cptr))
+ pptr->command[cmdlen++] = *cptr;
+ else if (cmdlen < (COMM_LEN - 4))
+ cmdlen +=
+ sprintf(&(pptr->command[cmdlen]),
+ "\\%03o", *cptr);
+ }
+ pptr->command[cmdlen] = '\0';
+ }
+ if (last_proc == NULL)
+ name_list->matched_procs = pptr;
+ else
+ last_proc->next = pptr;
+ if (pathname)
+ free(pathname);
+ if (fp)
+ fclose(fp);
+}
+
+/* Adds a knfsd etc process */
+static void add_special_proc(
+ struct names *name_list,
+ const char ptype,
+ const uid_t uid,
+ const char *command)
+{
+ struct procs *pptr;
+
+ for (pptr = name_list->matched_procs; pptr != NULL; pptr = pptr->next)
+ {
+ if (pptr->proc_type == ptype)
+ return;
+ }
+ if ((pptr = malloc(sizeof(struct procs))) == NULL)
+ {
+ fprintf(stderr,
+ _("Cannot allocate memory for matched proc: %s\n"),
+ strerror(errno));
+ return;
+ }
+ pptr->pid = 0;
+ pptr->uid = uid;
+ pptr->access = 0;
+ pptr->proc_type = ptype;
+ /* Append the special processes */
+ pptr->next = name_list->matched_procs;
+ name_list->matched_procs = pptr;
+ /* set command name */
+ pptr->command = strdup(command);
+}
+
+int parse_file(
+ struct names *this_name,
+ struct inode_list **ino_list,
+ const opt_type opts)
+{
+ char *new = expandpath(this_name->filename);
+ if (new)
+ {
+ if (this_name->filename)
+ free(this_name->filename);
+ this_name->filename = strdup(new);
+ }
+ if (statn(this_name->filename, STATX_INO|STATX_TYPE, &(this_name->st)) != 0 )
+ {
+ if (errno == ENOENT)
+ fprintf(stderr,
+ _("Specified filename %s does not exist.\n"),
+ this_name->filename);
+ else
+ fprintf(stderr, _("Cannot stat %s: %s\n"),
+ this_name->filename, strerror(errno));
+ return -1;
+ }
+#ifdef DEBUG
+ printf("adding file %s %lX %lX\n", this_name->filename,
+ (unsigned long)this_name->st.st_dev,
+ (unsigned long)this_name->st.st_ino);
+#endif /* DEBUG */
+ add_inode(ino_list, this_name, this_name->st.st_dev,
+ this_name->st.st_ino);
+ return 0;
+}
+
+int parse_unixsockets(
+ struct names *this_name,
+ struct inode_list **ino_list,
+ struct unixsocket_list *sun_head)
+{
+ struct unixsocket_list *sun_tmp;
+ dev_t net_dev;
+
+ net_dev = find_net_dev();
+
+ for (sun_tmp = sun_head; sun_tmp != NULL; sun_tmp = sun_tmp->next)
+ {
+ if (sun_tmp->dev == this_name->st.st_dev
+ && sun_tmp->inode == this_name->st.st_ino)
+ {
+ add_inode(ino_list, this_name, net_dev,
+ sun_tmp->net_inode);
+ return 0;
+ }
+ }
+ return 0;
+}
+
+int parse_mounts(
+ struct names *this_name,
+ struct device_list **dev_list,
+ const opt_type opts)
+{
+ dev_t match_device;
+
+ if (S_ISBLK(this_name->st.st_mode))
+ match_device = this_name->st.st_rdev;
+ else
+ match_device = this_name->st.st_dev;
+ add_device(dev_list, this_name, match_device);
+ return 0;
+}
+
+#ifdef WITH_IPV6
+int parse_inet(
+ struct names *this_name,
+ const int ipv4_only,
+ const int ipv6_only,
+ struct ip_connections **ip_list,
+ struct ip6_connections **ip6_list)
+#else
+int parse_inet(
+ struct names *this_name,
+ struct ip_connections **ip_list)
+#endif
+{
+ struct addrinfo *res, *resptr;
+ struct addrinfo hints;
+ int errcode;
+ char *lcl_port_str, *rmt_addr_str, *rmt_port_str, *tmpstr, *tmpstr2;
+ in_port_t lcl_port;
+ struct sockaddr_in *sin;
+#ifdef WITH_IPV6
+ struct sockaddr_in6 *sin6;
+#endif
+ char hostspec[100];
+ char *protocol;
+ int i;
+
+ if ((protocol = strchr(this_name->filename, '/')) == NULL)
+ return -1;
+ protocol++;
+ if (protocol[0] == '\0')
+ return -1;
+ for (i = 0;
+ i < 99 && this_name->filename[i] != '\0'
+ && this_name->filename[i] != '/'; i++)
+ hostspec[i] = this_name->filename[i];
+ hostspec[i] = '\0';
+
+ lcl_port_str = rmt_addr_str = rmt_port_str = NULL;
+ /* Split out the names */
+ if ((tmpstr = strchr(hostspec, ',')) == NULL)
+ {
+ /* Single option */
+ lcl_port_str = strdup(hostspec);
+ } else {
+ if (tmpstr == hostspec)
+ lcl_port_str = NULL;
+ else {
+ *tmpstr = '\0';
+ lcl_port_str = strdup(hostspec);
+ }
+ tmpstr++;
+ if (*tmpstr != '\0') {
+ if ((tmpstr2 = strchr(tmpstr, ',')) == NULL)
+ {
+ /* Only 2 options */
+ rmt_addr_str = tmpstr;
+ } else {
+ if (tmpstr2 == tmpstr)
+ rmt_addr_str = NULL;
+ else {
+ rmt_addr_str = tmpstr;
+ *tmpstr2 = '\0';
+ }
+ tmpstr2++;
+ if (*tmpstr2 != '\0')
+ rmt_port_str = tmpstr2;
+ }
+ }
+ }
+#ifdef DEBUG
+ printf("parsed to lp %s rh %s rp %s\n", lcl_port_str, rmt_addr_str,
+ rmt_port_str);
+#endif
+
+ memset(&hints, 0, sizeof(hints));
+#ifdef WITH_IPV6
+ if (ipv6_only)
+ {
+ hints.ai_family = PF_INET6;
+ } else if (ipv4_only)
+ {
+ hints.ai_family = PF_INET;
+ } else
+ hints.ai_family = PF_UNSPEC;
+#else
+ hints.ai_family = PF_INET;
+#endif
+ if (strcmp(protocol, "tcp") == 0)
+ hints.ai_socktype = SOCK_STREAM;
+ else
+ hints.ai_socktype = SOCK_DGRAM;
+
+ if (lcl_port_str == NULL)
+ {
+ lcl_port = 0;
+ } else {
+ /* Resolve local port first */
+ if ((errcode =
+ getaddrinfo(NULL, lcl_port_str, &hints, &res)) != 0)
+ {
+ fprintf(stderr, _("Cannot resolve local port %s: %s\n"),
+ lcl_port_str, gai_strerror(errcode));
+ free(lcl_port_str);
+ return -1;
+ }
+ free(lcl_port_str);
+ if (res == NULL)
+ return -1;
+ switch (res->ai_family)
+ {
+ case AF_INET:
+ lcl_port =
+ ((struct sockaddr_in *)(res->ai_addr))->sin_port;
+ break;
+#ifdef WITH_IPV6
+ case AF_INET6:
+ lcl_port =
+ ((struct sockaddr_in6 *)(res->ai_addr))->sin6_port;
+ break;
+#endif
+ default:
+ fprintf(stderr, _("Unknown local port AF %d\n"),
+ res->ai_family);
+ freeaddrinfo(res);
+ return -1;
+ }
+ freeaddrinfo(res);
+ }
+ res = NULL;
+ if (rmt_addr_str == NULL && rmt_port_str == NULL)
+ {
+ add_ip_conn(ip_list, protocol, this_name, ntohs(lcl_port), 0,
+ INADDR_ANY);
+#ifdef WITH_IPV6
+ add_ip6_conn(ip6_list, protocol, this_name, ntohs(lcl_port), 0,
+ in6addr_any);
+#endif
+ return 0;
+ } else {
+ /* Resolve remote address and port */
+ if (getaddrinfo(rmt_addr_str, rmt_port_str, &hints, &res) == 0)
+ {
+ for (resptr = res; resptr != NULL;
+ resptr = resptr->ai_next)
+ {
+ switch (resptr->ai_family)
+ {
+ case AF_INET:
+ sin = (struct sockaddr_in *)
+ resptr->ai_addr;
+ if (rmt_addr_str == NULL)
+ {
+ add_ip_conn(ip_list, protocol, this_name,
+ ntohs(lcl_port), ntohs(sin->sin_port),
+ INADDR_ANY);
+ } else {
+ add_ip_conn(ip_list, protocol, this_name,
+ ntohs(lcl_port), ntohs(sin->sin_port),
+ sin->sin_addr.s_addr);
+ }
+ break;
+#ifdef WITH_IPV6
+ case AF_INET6:
+ sin6 = (struct sockaddr_in6 *) resptr->ai_addr;
+ if (rmt_addr_str == NULL)
+ {
+ add_ip6_conn(ip6_list, protocol, this_name,
+ ntohs(lcl_port), ntohs(sin6->sin6_port),
+ in6addr_any);
+ } else {
+ add_ip6_conn(ip6_list, protocol, this_name,
+ ntohs(lcl_port), ntohs(sin6->sin6_port),
+ sin6->sin6_addr);
+ }
+ break;
+#endif
+ }
+ } /*while */
+ freeaddrinfo(res);
+ return 0;
+ }
+ }
+ return 1;
+}
+
+void find_net_sockets(
+ struct inode_list **ino_list,
+ struct ip_connections *conn_list,
+ const char *protocol,
+ dev_t netdev)
+{
+ FILE *fp;
+ char pathname[200], line[BUFSIZ];
+ unsigned long loc_port, rmt_port;
+ unsigned long rmt_addr;
+ unsigned long long scanned_inode;
+ ino_t inode;
+ struct ip_connections *conn_tmp;
+
+ if (snprintf(pathname, 200, "/proc/net/%s", protocol) < 0)
+ return;
+
+ if ((fp = fopen(pathname, "r")) == NULL)
+ {
+ fprintf(stderr, _("Cannot open protocol file \"%s\": %s\n"),
+ pathname, strerror(errno));
+ return;
+ }
+ while (fgets(line, BUFSIZ, fp) != NULL)
+ {
+ if (sscanf
+ (line,
+ "%*u: %*x:%lx %08lx:%lx %*x %*x:%*x %*x:%*x %*x %*d %*d %llu",
+ &loc_port, &rmt_addr, &rmt_port, &scanned_inode) != 4)
+ continue;
+#ifdef DEBUG
+ printf("Found IPv4 *:%lu with %s:%lu\n", loc_port,
+ inet_ntoa(*((struct in_addr *)&rmt_addr)), rmt_port);
+#endif /* DEBUG */
+ inode = scanned_inode;
+ for (conn_tmp = conn_list; conn_tmp != NULL;
+ conn_tmp = conn_tmp->next)
+ {
+#ifdef DEBUG
+ printf(" Comparing with *.%lu %s:%lu\n",
+ conn_tmp->lcl_port,
+ inet_ntoa(conn_tmp->rmt_address),
+ conn_tmp->rmt_port);
+#endif
+ if ((conn_tmp->lcl_port == 0
+ || conn_tmp->lcl_port == loc_port)
+ && (conn_tmp->rmt_port == 0
+ || conn_tmp->rmt_port == rmt_port)
+ && (conn_tmp->rmt_address.s_addr == INADDR_ANY
+ ||
+ (memcmp
+ (&(conn_tmp->rmt_address), &(rmt_addr),
+ 4) == 0)))
+ {
+ /* add inode to list */
+#ifdef DEBUG
+ printf("Added inode!\n");
+#endif /* DEBUG */
+ add_inode(ino_list, conn_tmp->name, netdev, inode);
+ }
+ }
+
+ }
+ fclose(fp);
+}
+
+#ifdef WITH_IPV6
+void find_net6_sockets(
+ struct inode_list **ino_list,
+ struct ip6_connections *conn_list,
+ const char *protocol,
+ const dev_t netdev)
+{
+ FILE *fp;
+ char pathname[200], line[BUFSIZ];
+ unsigned long loc_port, rmt_port;
+ struct in6_addr rmt_addr;
+ unsigned int tmp_addr[4];
+ char rmt_addr6str[INET6_ADDRSTRLEN];
+ struct ip6_connections *conn_tmp;
+ unsigned long long scanned_inode;
+ ino_t inode;
+
+ if (snprintf(pathname, 200, "/proc/net/%s6", protocol) < 0)
+ return;
+
+ if ((fp = fopen(pathname, "r")) == NULL)
+ {
+#ifdef DEBUG
+ printf("Cannot open protocol file \"%s\": %s\n", pathname,
+ strerror(errno));
+#endif /* DEBUG */
+ return;
+ }
+ while (fgets(line, BUFSIZ, fp) != NULL)
+ {
+ if (sscanf
+ (line,
+ "%*u: %*x:%lx %08x%08x%08x%08x:%lx %*x %*x:%*x %*x:%*x %*x %*d %*d %llu",
+ &loc_port, &(tmp_addr[0]), &(tmp_addr[1]), &(tmp_addr[2]),
+ &(tmp_addr[3]), &rmt_port, &scanned_inode) != 7)
+ continue;
+ inode = scanned_inode;
+ rmt_addr.s6_addr32[0] = tmp_addr[0];
+ rmt_addr.s6_addr32[1] = tmp_addr[1];
+ rmt_addr.s6_addr32[2] = tmp_addr[2];
+ rmt_addr.s6_addr32[3] = tmp_addr[3];
+ inet_ntop(AF_INET6, &rmt_addr, rmt_addr6str, INET6_ADDRSTRLEN);
+#ifdef DEBUG
+ printf("Found IPv6 %ld with %s:%ld\n", loc_port, rmt_addr6str,
+ rmt_port);
+#endif /* DEBUG */
+ for (conn_tmp = conn_list; conn_tmp != NULL;
+ conn_tmp = conn_tmp->next)
+ {
+ inet_ntop(AF_INET6, &conn_tmp->rmt_address,
+ rmt_addr6str, INET6_ADDRSTRLEN);
+#ifdef DEBUG
+ printf(" Comparing with *.%lu %s:%lu ...\n",
+ conn_tmp->lcl_port, rmt_addr6str,
+ conn_tmp->rmt_port);
+#endif /* DEBUG */
+ if ((conn_tmp->lcl_port == 0
+ || conn_tmp->lcl_port == loc_port)
+ && (conn_tmp->rmt_port == 0
+ || conn_tmp->rmt_port == rmt_port)
+ &&
+ (memcmp(&(conn_tmp->rmt_address), &in6addr_any, 16)
+ == 0
+ ||
+ (memcmp(&(conn_tmp->rmt_address), &(rmt_addr), 16)
+ == 0)))
+ {
+ add_inode(ino_list, conn_tmp->name, netdev, inode);
+ }
+ }
+ }
+ fclose(fp);
+}
+#endif
+
+static void read_proc_mounts(
+ struct mount_list **mnt_list)
+{
+ FILE *fp;
+ char line[BUFSIZ];
+ char *find_mountp;
+ char *find_space;
+ struct mount_list *mnt_tmp;
+
+ if ((fp = fopen(PROC_MOUNTS, "r")) == NULL)
+ {
+ fprintf(stderr, "Cannot open %s\n", PROC_MOUNTS);
+ return;
+ }
+ while (fgets(line, BUFSIZ, fp) != NULL)
+ {
+ if ((find_mountp = strchr(line, ' ')) == NULL)
+ continue;
+ find_mountp++;
+ if ((find_space = strchr(find_mountp, ' ')) == NULL)
+ continue;
+ *find_space = '\0';
+ if ((mnt_tmp = malloc(sizeof(struct mount_list))) == NULL)
+ continue;
+ if ((mnt_tmp->mountpoint = strdup(find_mountp)) == NULL)
+ {
+ free(mnt_tmp);
+ continue;
+ }
+ mnt_tmp->next = *mnt_list;
+ *mnt_list = mnt_tmp;
+ }
+ fclose(fp);
+}
+
+static void free_proc_mounts(
+ struct mount_list *mnt_list)
+{
+ struct mount_list *mnt_tmp, *mnt_next;
+
+ mnt_tmp = mnt_list;
+ while (mnt_tmp != NULL)
+ {
+ mnt_next = mnt_tmp->next;
+ free(mnt_tmp->mountpoint);
+ free(mnt_tmp);
+ mnt_tmp = mnt_next;
+ }
+}
+
+/*
+ * Free up structures allocated in add_matched_proc and add_special_proc
+ */
+static void free_matched_procs(
+ struct procs *matched_procs)
+{
+ struct procs *procs_tmp, *procs_next;
+
+ procs_tmp = matched_procs;
+ while (procs_tmp != NULL)
+ {
+ procs_next = procs_tmp->next;
+ if (procs_tmp->command)
+ free(procs_tmp->command);
+ free(procs_tmp);
+ procs_tmp = procs_next;
+ }
+}
+
+static void free_names()
+{
+ while(names_head != NULL)
+ {
+ struct names *this_name = names_head;
+ names_head = this_name->next;
+ free_matched_procs(this_name->matched_procs);
+ free(this_name->filename);
+ free(this_name);
+ }
+ names_tail = NULL;
+}
+
+/*
+ * Free up structures allocated in add_ip_conn and add_ip6_conn
+ */
+static void free_connection(
+ struct ip_connections **conn)
+{
+ struct ip_connections *conn_tmp, *conn_next;
+
+ conn_tmp = *conn;
+ while(conn_tmp != NULL)
+ {
+ conn_next = conn_tmp->next;
+ free(conn_tmp);
+ conn_tmp = conn_next;
+ }
+ *conn = NULL;
+}
+
+#ifdef WITH_IPV6
+static void free_connection6(
+ struct ip6_connections **conn)
+{
+ struct ip6_connections *conn_tmp, *conn_next;
+
+ conn_tmp = *conn;
+ while(conn_tmp != NULL)
+ {
+ conn_next = conn_tmp->next;
+ free(conn_tmp);
+ conn_tmp = conn_next;
+ }
+ *conn = NULL;
+}
+#endif
+
+/*
+ * Free up structures allocated in add_inode
+ */
+static void free_inodes(
+ struct inode_list **match_inodes)
+{
+ struct inode_list *inode_tmp, *inode_next;
+
+ inode_tmp = *match_inodes;
+ while(inode_tmp != NULL)
+ {
+ inode_next = inode_tmp->next;
+ free(inode_tmp);
+ inode_tmp = inode_next;
+ }
+ *match_inodes = NULL;
+}
+
+/*
+ * Free up structures allocated in add_device
+ */
+static void free_devices(
+ struct device_list **match_devices)
+{
+ struct device_list *device_tmp, *device_next;
+
+ device_tmp = *match_devices;
+ while(device_tmp != NULL)
+ {
+ device_next = device_tmp->next;
+ free(device_tmp);
+ device_tmp = device_next;
+ }
+ *match_devices = NULL;
+}
+
+static void atexit_free_lists()
+{
+ free_connection(&tcp_connection_list);
+ free_connection(&udp_connection_list);
+#ifdef WITH_IPV6
+ free_connection6(&tcp6_connection_list);
+ free_connection6(&udp6_connection_list);
+#endif
+
+ free_inodes(&match_inodes);
+ free_devices(&match_devices);
+
+ free_names();
+}
+
+
+static int is_mountpoint(
+ struct mount_list **mnt_list,
+ char *arg)
+{
+ char *p;
+ struct mount_list *mnt_tmp;
+
+ if (*arg == '\0')
+ return 0;
+ /* Remove trailing slashes. */
+ for (p = arg; *p != '\0'; p++)
+ /*do nothing*/;
+ while (*(--p) == '/' && p > arg)
+ *p = '\0';
+
+ for (mnt_tmp = *mnt_list; mnt_tmp != NULL; mnt_tmp = mnt_tmp->next)
+ if (!strcmp(mnt_tmp->mountpoint, arg))
+ return 1;
+ return 0;
+}
+
+static void check_mountpoints(
+ struct mount_list **mnt_list,
+ struct names **head,
+ struct names **tail)
+{
+ struct names *this, *last;
+
+ last = NULL;
+ for(this = *head; this != NULL; this = this->next)
+ {
+ if (this->name_space == NAMESPACE_FILE &&
+ !is_mountpoint(mnt_list, this->filename))
+ {
+ fprintf(stderr,
+ _("Specified filename %s is not a mountpoint.\n"),
+ this->filename);
+ /* Remove from list */
+ if (last)
+ last->next = this->next;
+ if (*head == this)
+ *head = this->next;
+ if (*tail == this)
+ *tail = last;
+ } else {
+ last = this;
+ }
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ opt_type opts;
+ int sig_number;
+#ifdef WITH_IPV6
+ int ipv4_only, ipv6_only;
+#endif
+ unsigned char default_namespace = NAMESPACE_FILE;
+
+ dev_t netdev;
+ struct names *this_name;
+ int argc_cnt;
+ char *current_argv, *option;
+ char option_buf[3];
+ struct option *optr;
+ char *nsptr;
+ int skip_argv;
+
+ struct option options[] = {
+ {"all", 0, NULL, 'a'},
+ {"kill", 0, NULL, 'k'},
+ {"interactive", 0, NULL, 'i'},
+ {"inode", 0, NULL, 'I'},
+ {"list-signals", 0, NULL, 'l'},
+ {"mount", 0, NULL, 'm'},
+ {"ismountpoint", 0, NULL, 'M'},
+ {"namespace", 1, NULL, 'n'},
+ {"silent", 0, NULL, 's'},
+ {"user", 0, NULL, 'u'},
+ {"verbose", 0, NULL, 'v'},
+ {"writeonly", 0, NULL, 'w'},
+ {"version", 0, NULL, 'V'},
+#ifdef WITH_IPV6
+ {"ipv4", 0, NULL, '4'},
+ {"ipv6", 0, NULL, '6'},
+#endif
+ {0, 0, 0, 0}
+ };
+
+#ifdef WITH_IPV6
+ ipv4_only = ipv6_only = 0;
+#endif
+ this_name = NULL;
+ opts = 0;
+ sig_number = SIGKILL;
+
+#ifdef ENABLE_NLS
+ /* Set up the i18n */
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+#endif
+
+ netdev = find_net_dev();
+#ifndef __CYGWIN__ /* Cygwin doesn't support /proc/net/unix */
+ fill_unix_cache(&unixsockets);
+ atexit(atexit_clear_unix_cache);
+#endif
+ atexit(atexit_free_lists);
+
+ for (argc_cnt = 1; argc_cnt < argc; argc_cnt++)
+ {
+ current_argv = argv[argc_cnt];
+ if (current_argv[0] == '-') /* its an option */
+ {
+ if (current_argv[1] == '-') /* its a long option */
+ {
+ if (current_argv[2] == '\0') /* -- */
+ break;
+ /* Parse the long options */
+ option = option_buf;
+ for (optr = options; optr->name != NULL; optr++)
+ {
+ if (strcmp(current_argv + 2, optr->name) == 0)
+ {
+ sprintf(option_buf, "-%c", (char)optr->val);
+ break;
+ }
+ }
+ if (optr->name == NULL)
+ {
+ fprintf(stderr, _("%s: Invalid option %s\n"),
+ argv[0], current_argv);
+ usage(NULL);
+ }
+ } else {
+ option = current_argv;
+ }
+ skip_argv = 0;
+ while (*(++option) != '\0' && !skip_argv)/* skips over the - */
+ {
+ switch (*option)
+ {
+#ifdef WITH_IPV6
+ case '4':
+ ipv4_only = 1;
+ break;
+ case '6':
+ ipv6_only = 1;
+ break;
+#endif /* WITH_IPV6 */
+ case 'a':
+ opts |= OPT_ALLFILES;
+ break;
+ case 'c':
+ opts |= OPT_MOUNTS;
+ break;
+ case 'f':
+ /* ignored */
+ break;
+ case 'h':
+ usage(NULL);
+ break;
+ case 'i':
+ opts |= OPT_INTERACTIVE;
+ break;
+ case 'I':
+ opts |= OPT_ALWAYSSTAT;
+ break;
+ case 'k':
+ opts |= OPT_KILL;
+ break;
+ case 'l':
+ list_signals();
+ return 0;
+ case 'm':
+ opts |= OPT_MOUNTS;
+ break;
+ case 'M':
+ opts |= OPT_ISMOUNTPOINT;
+ break;
+ case 'n':
+ argc_cnt++;
+ if (argc_cnt >= argc)
+ {
+ usage(_
+ ("Namespace option requires an argument."));
+ }
+ skip_argv = 1;
+ //while(option != '\0') option++;
+ if (strcmp(argv[argc_cnt], "tcp") == 0)
+ default_namespace = NAMESPACE_TCP;
+ else if (strcmp(argv[argc_cnt], "udp") == 0)
+ default_namespace = NAMESPACE_UDP;
+ else if (strcmp(argv[argc_cnt], "file") == 0)
+ default_namespace = NAMESPACE_FILE;
+ else
+ usage(_
+ ("Invalid namespace name"));
+ break;
+ case 's':
+ opts |= OPT_SILENT;
+ break;
+ case 'u':
+ opts |= OPT_USER;
+ break;
+ case 'v':
+ opts |= OPT_VERBOSE;
+ break;
+ case 'w':
+ opts |= OPT_WRITE;
+ break;
+ case 'V':
+ print_version();
+ return 0;
+ default:
+ if (isupper(*option) || isdigit(*option))
+ {
+ sig_number = get_signal(current_argv + 1, argv[0]);
+ skip_argv = 1;
+ break;
+ }
+ fprintf(stderr, "%s: Invalid option %c\n",
+ argv[0], *option);
+ usage(NULL);
+ break;
+ } /* switch */
+ } /* while option */
+ continue;
+ }
+
+#if defined(HAVE_DECL_SYS_STATX) && HAVE_DECL_SYS_STATX == 1
+ if ((opts & OPT_ALWAYSSTAT))
+ stat_flags = 0; /* Triggers sync with e.g. remote NFS server even on autofs */
+#endif
+ /* an option */
+ /* Not an option, must be a file specification */
+ if ((this_name = malloc(sizeof(struct names))) == NULL)
+ continue;
+ this_name->next = NULL;
+ /* try to find namespace spec */
+ this_name->name_space = default_namespace;
+ if (((nsptr = strchr(current_argv, '/')) != NULL)
+ && (nsptr != current_argv))
+ {
+ if (strcmp(nsptr + 1, "tcp") == 0)
+ {
+ this_name->name_space = NAMESPACE_TCP;
+ *nsptr = '\0';
+ } else if (strcmp(nsptr + 1, "udp") == 0)
+ {
+ this_name->name_space = NAMESPACE_UDP;
+ *nsptr = '\0';
+ } else if (strcmp(nsptr + 1, "file") == 0)
+ {
+ this_name->name_space = NAMESPACE_FILE;
+ *nsptr = '\0';
+ }
+ }
+ this_name->matched_procs = NULL;
+ if (opts & (OPT_MOUNTS | OPT_ISMOUNTPOINT)
+ && this_name->name_space != NAMESPACE_FILE)
+ {
+ free(this_name);
+ usage(_
+ ("You can only use files with mountpoint options"));
+ }
+ switch (this_name->name_space)
+ {
+ case NAMESPACE_TCP:
+ if (asprintf
+ (&(this_name->filename), "%s/tcp",
+ current_argv) > 0)
+ {
+#ifdef WITH_IPV6
+ parse_inet(this_name, ipv4_only, ipv6_only,
+ &tcp_connection_list, &tcp6_connection_list);
+#else
+ parse_inet(this_name, &tcp_connection_list);
+#endif
+ }
+ break;
+ case NAMESPACE_UDP:
+ if (asprintf (&(this_name->filename), "%s/udp", current_argv) > 0)
+ {
+#ifdef WITH_IPV6
+ parse_inet(this_name, ipv4_only, ipv6_only,
+ &udp_connection_list, &udp6_connection_list);
+#else
+ parse_inet(this_name, &udp_connection_list);
+#endif
+ }
+ break;
+ default: /* FILE */
+ this_name->filename = strdup(current_argv);
+ if (parse_file(this_name, &match_inodes, opts) == 0)
+ {
+ if (opts & OPT_MOUNTS)
+ parse_mounts(this_name, &match_devices, opts);
+ else
+ parse_unixsockets(this_name, &match_inodes, unixsockets);
+ }
+ break;
+ }
+
+ if (names_head == NULL)
+ names_head = this_name;
+ if (names_tail != NULL)
+ names_tail->next = this_name;
+ names_tail = this_name;
+ } /* for across the argvs */
+ if (names_head == NULL)
+ usage(_("No process specification given"));
+
+ /* Check if -M flag was used and if so check mounts */
+ if (opts & OPT_ISMOUNTPOINT)
+ {
+ struct mount_list *mounts = NULL;
+ read_proc_mounts(&mounts);
+ check_mountpoints(&mounts, &names_head, &names_tail);
+ free_proc_mounts(mounts);
+ }
+
+ if (opts & OPT_SILENT)
+ {
+ opts &= ~OPT_VERBOSE;
+ opts &= ~OPT_USER;
+ if (opts & OPT_ALLFILES)
+ usage(_
+ ("all option cannot be used with silent option."));
+ }
+#ifdef WITH_IPV6
+ if (ipv4_only && ipv6_only)
+ usage(_
+ ("You cannot search for only IPv4 and only IPv6 sockets at the same time"));
+ if (!ipv6_only)
+ {
+#endif
+ if (tcp_connection_list != NULL)
+ find_net_sockets(&match_inodes, tcp_connection_list, "tcp", netdev);
+ if (udp_connection_list != NULL)
+ find_net_sockets(&match_inodes, udp_connection_list, "udp", netdev);
+#ifdef WITH_IPV6
+ }
+ if (!ipv4_only)
+ {
+ if (tcp6_connection_list != NULL)
+ find_net6_sockets(&match_inodes, tcp6_connection_list,
+ "tcp", netdev);
+ if (udp6_connection_list != NULL)
+ find_net6_sockets(&match_inodes, udp6_connection_list,
+ "udp", netdev);
+ }
+#endif
+ free_connection(&tcp_connection_list);
+ free_connection(&udp_connection_list);
+#ifdef WITH_IPV6
+ free_connection6(&tcp6_connection_list);
+ free_connection6(&udp6_connection_list);
+#endif
+#ifdef DEBUG
+ debug_match_lists(names_head, match_inodes, match_devices);
+#endif
+ scan_procs(names_head, match_inodes, match_devices, unixsockets, netdev);
+#ifndef __CYGWIN__ /* Cygwin doesn't support /proc/net/unix */
+ clear_unix_cache(&unixsockets);
+#endif
+ scan_knfsd(names_head, match_inodes, match_devices);
+ scan_mounts(names_head, match_inodes, match_devices);
+ scan_swaps(names_head, match_inodes, match_devices);
+ free_inodes(&match_inodes);
+ free_devices(&match_devices);
+ return print_matches(names_head, opts, sig_number);
+}
+
+/*
+ * returns 0 if match, 1 if no match
+ */
+static int print_matches(
+ struct names *names_head,
+ const opt_type opts,
+ const int sig_number)
+{
+ struct names *nptr;
+ struct procs *pptr;
+ char head = 0;
+ char first = 1;
+ int len = 0;
+ struct passwd *pwent = NULL;
+ int have_match = 0;
+ int have_kill = 0;
+ int name_has_procs = 0;
+
+ for (nptr = names_head; nptr != NULL; nptr = nptr->next)
+ {
+ if (opts & OPT_SILENT)
+ {
+ for (pptr = nptr->matched_procs; pptr != NULL;
+ pptr = pptr->next)
+ {
+ if (pptr->proc_type != PTYPE_NORMAL)
+ continue;
+
+ have_match = 1;
+ }
+ } else { /* We're not silent */
+ if ((opts & OPT_ALLFILES) == 0)
+ {
+ name_has_procs = 0;
+ if (opts & OPT_VERBOSE)
+ {
+ if (nptr->matched_procs)
+ name_has_procs = 1;
+ } else {
+ for (pptr = nptr->matched_procs;
+ pptr != NULL; pptr = pptr->next)
+ {
+ if (pptr->proc_type == PTYPE_NORMAL)
+ {
+ name_has_procs = 1;
+ break;
+ }
+ }
+ }
+ }
+ if (name_has_procs == 1 || opts & OPT_ALLFILES)
+ {
+ if (head == 0 && opts & OPT_VERBOSE)
+ {
+ fprintf(stderr,
+ _("%*s USER PID ACCESS COMMAND\n"), NAME_FIELD, "");
+ head = 1;
+ }
+
+ fprintf(stderr, "%s:", nptr->filename);
+ len = strlen(nptr->filename) + 1;
+ }
+
+ first = 1;
+ for (pptr = nptr->matched_procs; pptr != NULL; pptr = pptr->next)
+ {
+ /* Suppress any special "processes" */
+ if (!(opts & OPT_VERBOSE)
+ && (pptr->proc_type != PTYPE_NORMAL))
+ continue;
+
+ have_match = 1;
+ if (opts & (OPT_VERBOSE | OPT_USER))
+ {
+ if (pwent == NULL || pwent->pw_uid != pptr->uid)
+ pwent = getpwuid(pptr->uid);
+ }
+ if (len > NAME_FIELD && (opts & OPT_VERBOSE))
+ {
+ putc('\n', stderr);
+ len = 0;
+ }
+ if ((opts & OPT_VERBOSE) || first)
+ while (len++ < NAME_FIELD)
+ putc(' ', stderr);
+ if (opts & OPT_VERBOSE)
+ {
+ if (pwent == NULL)
+ fprintf(stderr, " %-8s ",
+ _("(unknown)"));
+ else
+ fprintf(stderr, " %-8s ",
+ pwent->pw_name);
+ }
+ if (pptr->proc_type == PTYPE_NORMAL)
+ printf(" %5d", pptr->pid);
+ else
+ printf("kernel");
+ fflush(stdout);
+ if (opts & OPT_VERBOSE)
+ {
+ switch (pptr->proc_type)
+ {
+ case PTYPE_KNFSD:
+ fprintf(stderr, " knfsd ");
+ break;
+ case PTYPE_MOUNT:
+ fprintf(stderr, " mount ");
+ break;
+ case PTYPE_SWAP:
+ fprintf(stderr, " swap ");
+ break;
+ default:
+ fprintf(stderr, " %c%c%c%c%c ",
+ pptr->access & ACCESS_FILE ?
+ (pptr->access & ACCESS_FILEWR ? 'F' : 'f') : '.',
+ pptr->access & ACCESS_ROOT ? 'r' : '.',
+ pptr->access & ACCESS_CWD ? 'c' : '.',
+ pptr->access & ACCESS_EXE ? 'e' : '.',
+ (pptr->access & ACCESS_MMAP)
+ && !(pptr-> access & ACCESS_EXE) ? 'm' : '.');
+ } /* switch */
+ } else {
+ if (pptr->access & ACCESS_ROOT)
+ putc('r', stderr);
+ if (pptr->access & ACCESS_CWD)
+ putc('c', stderr);
+ if (pptr->access & ACCESS_EXE)
+ putc('e', stderr);
+ else if (pptr->access & ACCESS_MMAP)
+ putc('m', stderr);
+ }
+ if (opts & OPT_USER)
+ {
+ if (pwent == NULL)
+ fprintf(stderr, " %-8s ", _("(unknown)"));
+ else
+ fprintf(stderr, "(%s)", pwent->pw_name);
+ }
+ if (opts & OPT_VERBOSE)
+ {
+ if (pptr->command == NULL)
+ fprintf(stderr, "???\n");
+ else
+ fprintf(stderr, "%s\n", pptr->command);
+ }
+ len = 0;
+ first = 0;
+ }
+ if (opts & OPT_VERBOSE)
+ {
+ /* put a newline if showing all files and no procs */
+ if (nptr->matched_procs == NULL && (opts & OPT_ALLFILES))
+ putc('\n', stderr);
+ } else {
+ if (name_has_procs || (opts & OPT_ALLFILES))
+ putc('\n', stderr);
+ }
+ } /* be silent */
+ if (opts & OPT_KILL)
+ have_kill |= kill_matched_proc(nptr->matched_procs,
+ opts, sig_number);
+
+ } /* next name */
+ if (opts & OPT_KILL)
+ return (have_kill == 1 ? 0 : 1);
+ else
+ return (have_match == 1 ? 0 : 1);
+
+}
+
+static struct stat *get_pidstat(
+ const pid_t pid,
+ const char *filename)
+{
+ char pathname[PATH_MAX];
+ struct stat *st;
+
+ if ((st = (struct stat *)malloc(sizeof(struct stat))) == NULL)
+ return NULL;
+ snprintf(pathname, PATH_MAX-1, "/proc/%d/%s", pid, filename);
+ if (statn(pathname, STATX_UID|STATX_INO|STATX_TYPE, st) != 0)
+ {
+ free(st);
+ return NULL;
+ }
+ return st;
+}
+
+static void check_dir(
+ const pid_t pid,
+ const char *dirname,
+ struct device_list *dev_head,
+ struct inode_list *ino_head,
+ const uid_t uid,
+ const char access,
+ struct unixsocket_list *sockets,
+ dev_t netdev)
+{
+ DIR *dirp;
+ dev_t thedev;
+ struct dirent *direntry;
+ struct inode_list *ino_tmp;
+ struct device_list *dev_tmp;
+ struct unixsocket_list *sock_tmp;
+ struct stat st, lst;
+ char *dirpath;
+ char filepath[PATH_MAX];
+ char real_filepath[PATH_MAX];
+
+ if (asprintf(&dirpath, "/proc/%d/%s", pid, dirname) < 0)
+ return;
+ if ((dirp = opendir(dirpath)) == NULL)
+ {
+ free(dirpath);
+ return;
+ }
+ free(dirpath);
+
+ while ((direntry = readdir(dirp)) != NULL)
+ {
+ if (direntry->d_name[0] < '0' || direntry->d_name[0] > '9')
+ continue;
+
+ snprintf(filepath, sizeof filepath - 1, "/proc/%d/%s/%s",
+ pid, dirname, direntry->d_name);
+
+ if (statn(filepath, STATX_INO, &st) != 0)
+ {
+ if (errno != ENOENT && errno != ENOTDIR && errno != EACCES)
+ {
+ fprintf(stderr, _("Cannot stat file %s: %s\n"),
+ filepath, strerror(errno));
+ }
+ } else {
+ thedev = st.st_dev;
+ if (thedev == netdev)
+ {
+ for (sock_tmp = sockets; sock_tmp != NULL;
+ sock_tmp = sock_tmp->next)
+ {
+ if (sock_tmp->net_inode == st.st_ino)
+ {
+ st.st_ino = sock_tmp->inode;
+ st.st_dev = sock_tmp->dev;
+ thedev = sock_tmp->dev;
+ break;
+ }
+ }
+ }
+ for (dev_tmp = dev_head; dev_tmp != NULL;
+ dev_tmp = dev_tmp->next)
+ {
+ if (thedev != dev_tmp->device)
+ continue;
+
+ /* check the paths match if it is not a block device or socket */
+ if (! S_ISBLK(dev_tmp->name->st.st_mode)
+ && !S_ISSOCK(st.st_mode))
+ {
+ if (readlink(filepath, real_filepath, PATH_MAX-1) < 0)
+ {
+ if (strncmp(dev_tmp->name->filename, filepath,
+ strlen(dev_tmp->name->filename)) != 0)
+ continue;
+ } else {
+ if (strncmp(dev_tmp->name->filename, real_filepath,
+ strlen(dev_tmp->name->filename)) != 0)
+ continue;
+ }
+ }
+ if (access == ACCESS_FILE
+ && (lstat(filepath, &lst) == 0)
+ && (lst.st_mode & S_IWUSR))
+ {
+ add_matched_proc(dev_tmp->name, pid, uid,
+ ACCESS_FILEWR | access);
+ } else {
+ add_matched_proc(dev_tmp->name, pid, uid, access);
+ }
+ }
+ for (ino_tmp = ino_head; ino_tmp != NULL; ino_tmp = ino_tmp->next)
+ {
+ if (thedev != ino_tmp->device)
+ continue;
+ if (!st.st_ino && statn(filepath, STATX_INO, &st) != 0)
+ {
+ fprintf(stderr, _("Cannot stat file %s: %s\n"),
+ filepath, strerror(errno));
+ continue;
+ }
+ if (st.st_ino == ino_tmp->inode)
+ {
+ if (access == ACCESS_FILE
+ && (lstat(filepath, &lst) == 0)
+ && (lst.st_mode & S_IWUSR))
+ {
+ add_matched_proc(ino_tmp->name, pid, uid,
+ ACCESS_FILEWR | access);
+ } else {
+ add_matched_proc(ino_tmp->name, pid, uid, access);
+ }
+ }
+ }
+ }
+ } /* while fd_dent */
+ closedir(dirp);
+}
+
+static void check_map(
+ const pid_t pid,
+ const char *filename,
+ struct device_list *dev_head,
+ struct inode_list *ino_head,
+ const uid_t uid,
+ const char access)
+{
+ char *pathname;
+ char line[BUFSIZ];
+ struct inode_list *ino_tmp;
+ struct device_list *dev_tmp;
+ FILE *fp;
+ unsigned long long tmp_inode;
+ unsigned int tmp_maj, tmp_min;
+ dev_t tmp_device;
+
+ if (asprintf(&pathname, "/proc/%d/%s", pid, filename) < 0)
+ return;
+ if ((fp = fopen(pathname, "r")) == NULL)
+ {
+ free(pathname);
+ return;
+ }
+ free(pathname);
+ while (fgets(line, BUFSIZ, fp))
+ {
+ if (sscanf(line, "%*s %*s %*s %x:%x %lld",
+ &tmp_maj, &tmp_min, &tmp_inode) == 3)
+ {
+ tmp_device = tmp_maj * 256 + tmp_min;
+ for (dev_tmp = dev_head; dev_tmp != NULL; dev_tmp = dev_tmp->next)
+ if (dev_tmp->device == tmp_device)
+ add_matched_proc(dev_tmp->name, pid, uid, access);
+ for (ino_tmp = ino_head; ino_tmp != NULL; ino_tmp = ino_tmp->next)
+ if (ino_tmp->device == tmp_device
+ && ino_tmp->inode == tmp_inode)
+ add_matched_proc(ino_tmp->name, pid, uid, access);
+ }
+ }
+ fclose(fp);
+}
+
+static uid_t getpiduid(
+ const pid_t pid)
+{
+ char *pathname;
+ struct stat st;
+
+ if (asprintf(&pathname, "/proc/%d", pid) < 0)
+ return 0;
+ if (statn(pathname, STATX_UID, &st) != 0)
+ {
+ free(pathname);
+ return 0;
+ }
+ free(pathname);
+ return st.st_uid;
+}
+
+/*
+ * fill_unix_cache : Create a list of Unix sockets
+ * This list is used later for matching purposes
+ */
+void fill_unix_cache(
+ struct unixsocket_list **unixsocket_head)
+{
+ FILE *fp;
+ char line[BUFSIZ];
+ unsigned long long scanned_inode;
+ struct stat st;
+ struct unixsocket_list *newsocket;
+
+ if ((fp = fopen("/proc/net/unix", "r")) == NULL)
+ {
+ fprintf(stderr, _("Cannot open /proc/net/unix: %s\n"),
+ strerror(errno));
+ return;
+ }
+ while (fgets(line, BUFSIZ, fp) != NULL)
+ {
+ char *path;
+ char *scanned_path = NULL;
+ if (sscanf(line, "%*x: %*x %*x %*x %*x %*d %llu %ms",
+ &scanned_inode, &scanned_path) != 2)
+ {
+ if (scanned_path)
+ free(scanned_path);
+ continue;
+ }
+ if (scanned_path == NULL)
+ continue;
+ path = scanned_path;
+ if (*scanned_path == '@')
+ scanned_path++;
+ if (statn(scanned_path, STATX_INO, &st) < 0)
+ {
+ free(path);
+ continue;
+ }
+ if ((newsocket = (struct unixsocket_list *)
+ malloc(sizeof(struct unixsocket_list))) == NULL)
+ {
+ free(path);
+ continue;
+ }
+ newsocket->sun_name = strdup(scanned_path);
+ newsocket->inode = st.st_ino;
+ newsocket->dev = st.st_dev;
+ newsocket->net_inode = scanned_inode;
+ newsocket->next = *unixsocket_head;
+ *unixsocket_head = newsocket;
+ free(path);
+ } /* while */
+
+ fclose(fp);
+}
+
+/*
+ * Free up the list of Unix sockets
+ */
+void clear_unix_cache(
+ struct unixsocket_list **unixsocket_head)
+{
+ while(*unixsocket_head != NULL)
+ {
+ struct unixsocket_list *oldsocket = *unixsocket_head;
+ *unixsocket_head = oldsocket->next;
+ free(oldsocket->sun_name);
+ free(oldsocket);
+ }
+}
+
+static void atexit_clear_unix_cache()
+{
+ clear_unix_cache(&unixsockets);
+}
+
+#ifdef DEBUG
+/* often not used, doesn't need translation */
+static void debug_match_lists(
+ struct names *names_head,
+ struct inode_list *ino_head,
+ struct device_list *dev_head)
+{
+ struct names *nptr;
+ struct inode_list *iptr;
+ struct device_list *dptr;
+
+ fprintf(stderr, "Specified Names:\n");
+ for (nptr = names_head; nptr != NULL; nptr = nptr->next)
+ {
+ fprintf(stderr, "\t%s %c\n", nptr->filename, nptr->name_space);
+ }
+ fprintf(stderr, "\nInodes:\n");
+ for (iptr = ino_head; iptr != NULL; iptr = iptr->next)
+ {
+ fprintf(stderr, " Dev:%0lx Inode:(%0ld) 0x%0lx => %s\n",
+ (unsigned long)iptr->device, (unsigned long)iptr->inode,
+ (unsigned long)iptr->inode, iptr->name->filename);
+ }
+ fprintf(stderr, "\nDevices:\n");
+ for (dptr = dev_head; dptr != NULL; dptr = dptr->next)
+ {
+ fprintf(stderr, "\tDev:%0lx\n", (unsigned long)dptr->device);
+ }
+}
+
+#endif
+
+/* 0 = no, 1=yes */
+static int ask(
+ const pid_t pid)
+{
+ int res;
+ size_t len = 0;
+ char *line = NULL;
+
+ fflush(stdout);
+ while (1)
+ {
+ fprintf(stderr, _("Kill process %d ? (y/N) "), pid);
+ fflush(stderr);
+ if (getline(&line, &len, stdin) < 0)
+ return 0;
+ if (line[0] == '\n')
+ {
+ free(line);
+ return 0;
+ }
+ res = rpmatch(line);
+ if (res >= 0)
+ {
+ free(line);
+ return res;
+ }
+ } /* while */
+}
+
+static int kill_matched_proc(
+ struct procs *proc_head,
+ const opt_type opts,
+ const int sig_number)
+{
+ struct procs *pptr;
+ pid_t mypid;
+ int ret = 0;
+
+ mypid = getpid();
+
+ for (pptr = proc_head; pptr != NULL; pptr = pptr->next)
+ {
+ if (pptr->pid == mypid)
+ continue; /* dont kill myself */
+ if (pptr->proc_type != PTYPE_NORMAL)
+ continue;
+ if ((opts & OPT_WRITE) && ((pptr->access & ACCESS_FILEWR) == 0))
+ continue;
+ if ((opts & OPT_INTERACTIVE) && (ask(pptr->pid) == 0))
+ continue;
+ if (kill(pptr->pid, sig_number) < 0)
+ {
+ fprintf(stderr, _("Could not kill process %d: %s\n"),
+ pptr->pid, strerror(errno));
+ continue;
+ }
+ ret = 1;
+ }
+ return ret;
+}
+
+static dev_t find_net_dev(void)
+{
+ int skt;
+ struct stat st;
+
+ if ((skt = socket(PF_INET, SOCK_DGRAM, 0)) < 0)
+ {
+ fprintf(stderr, _("Cannot open a network socket.\n"));
+ return -1;
+ }
+ if (fstatn(skt, STATX_INO, &st) != 0)
+ {
+ fprintf(stderr, _("Cannot find socket's device number.\n"));
+ close(skt);
+ return -1;
+ }
+ close(skt);
+ return st.st_dev;
+}
+
+static void scan_knfsd(
+ struct names *names_head,
+ struct inode_list *ino_head,
+ struct device_list *dev_head)
+{
+ struct device_list *dev_tmp;
+ struct inode_list *ino_tmp;
+ FILE *fp;
+ char line[BUFSIZ];
+ char *find_space;
+ struct stat st;
+
+ if ( (ino_head == NULL) && (dev_head == NULL) )
+ return;
+
+
+ if ((fp = fopen(KNFSD_EXPORTS, "r")) == NULL)
+ {
+#ifdef DEBUG
+ printf("Cannot open %s\n", KNFSD_EXPORTS);
+#endif
+ return;
+ }
+ while (fgets(line, BUFSIZ, fp) != NULL)
+ {
+ if (line[0] == '#')
+ continue;
+
+ if ((find_space = strpbrk(line, " \t")) == NULL)
+ continue;
+
+ *find_space = '\0';
+ if (statn(line, STATX_INO, &st) != 0)
+ continue;
+
+ /* Scan the devices */
+ for (dev_tmp = dev_head; dev_tmp != NULL;
+ dev_tmp = dev_tmp->next)
+ {
+ if (st.st_dev == dev_tmp->device)
+ add_special_proc(dev_tmp->name, PTYPE_KNFSD, 0, line);
+ }
+
+ for (ino_tmp = ino_head; ino_tmp != NULL;
+ ino_tmp = ino_tmp->next)
+ {
+ if (st.st_dev == ino_tmp->device
+ && st.st_ino == ino_tmp->inode)
+ add_special_proc(ino_tmp->name, PTYPE_KNFSD, 0, line);
+ }
+ }
+ fclose(fp);
+}
+
+static void scan_mounts(
+ struct names *names_head,
+ struct inode_list *ino_head,
+ struct device_list *dev_head)
+{
+ struct device_list *dev_tmp;
+ struct inode_list *ino_tmp;
+ FILE *fp;
+ char line[BUFSIZ];
+ char *find_mountp;
+ char *find_space;
+ struct stat st;
+
+ if ( (ino_head == NULL) && (dev_head == NULL) )
+ return;
+
+
+ if ((fp = fopen(PROC_MOUNTS, "r")) == NULL)
+ {
+ fprintf(stderr, "Cannot open %s\n", PROC_MOUNTS);
+ return;
+ }
+ while (fgets(line, BUFSIZ, fp) != NULL)
+ {
+ if ((find_mountp = strchr(line, ' ')) == NULL)
+ continue;
+ find_mountp++;
+ if ((find_space = strchr(find_mountp, ' ')) == NULL)
+ continue;
+ *find_space = '\0';
+ if (statn(find_mountp, STATX_INO, &st) != 0)
+ continue;
+ /* Scan the devices */
+ for (dev_tmp = dev_head; dev_tmp != NULL;
+ dev_tmp = dev_tmp->next)
+ {
+ if (st.st_dev == dev_tmp->device)
+ add_special_proc(dev_tmp->name, PTYPE_MOUNT, 0, find_mountp);
+ }
+ for (ino_tmp = ino_head; ino_tmp != NULL;
+ ino_tmp = ino_tmp->next)
+ {
+ if (st.st_dev == ino_tmp->device
+ && st.st_ino == ino_tmp->inode)
+ add_special_proc(ino_tmp->name, PTYPE_MOUNT, 0, find_mountp);
+ }
+ }
+ fclose(fp);
+}
+
+static void scan_swaps(
+ struct names *names_head,
+ struct inode_list *ino_head,
+ struct device_list *dev_head)
+{
+ struct device_list *dev_tmp;
+ struct inode_list *ino_tmp;
+ FILE *fp;
+ char line[BUFSIZ];
+ char *find_space;
+ struct stat st;
+
+ if ( (ino_head == NULL) && (dev_head == NULL) )
+ return;
+
+ if ((fp = fopen(PROC_SWAPS, "r")) == NULL)
+ {
+ /*fprintf(stderr, "Cannot open %s\n", PROC_SWAPS); */
+ return;
+ }
+ /* lines are filename type */
+ while (fgets(line, BUFSIZ, fp) != NULL)
+ {
+ if ((find_space = strchr(line, ' ')) == NULL)
+ continue;
+ *find_space = '\0';
+ find_space++;
+ while (*find_space == ' ')
+ {
+ find_space++;
+ if (*find_space == '\0')
+ continue;
+ }
+ if (statn(line, STATX_INO, &st) != 0)
+ continue;
+
+ /* Scan the devices */
+ for (dev_tmp = dev_head; dev_tmp != NULL;
+ dev_tmp = dev_tmp->next)
+ {
+ if (st.st_dev == dev_tmp->device)
+ add_special_proc(dev_tmp->name, PTYPE_SWAP, 0, line);
+ }
+ for (ino_tmp = ino_head; ino_tmp != NULL;
+ ino_tmp = ino_tmp->next)
+ {
+ if (st.st_dev == ino_tmp->device
+ && st.st_ino == ino_tmp->inode)
+ add_special_proc(ino_tmp->name, PTYPE_SWAP, 0, line);
+ }
+ }
+ fclose(fp);
+}
+
+/*
+ * Somehow the realpath(3) glibc function call, nevertheless
+ * it avoids lstat(2) system calls.
+ */
+static char real[PATH_MAX + 1];
+char *expandpath(
+ const char *path)
+{
+ char tmpbuf[PATH_MAX + 1];
+ const char *start, *end;
+ char *curr, *dest;
+ int deep = MAXSYMLINKS;
+
+ if (!path || *path == '\0')
+ return (char *)0;
+
+ curr = &real[0];
+
+ if (*path != '/')
+ {
+ if (!getcwd(curr, PATH_MAX))
+ return (char *)0;
+ dest = strchr(curr, '\0');
+ } else {
+ *curr = '/';
+ dest = curr + 1;
+ }
+
+ for (start = end = path; *start; start = end)
+ {
+ while (*start == '/')
+ ++start;
+
+ for (end = start; *end && *end != '/'; ++end) ;
+
+ if (end - start == 0)
+ break;
+ else if (end - start == 1 && start[0] == '.')
+ {
+ ;
+ } else if (end - start == 2 && start[0] == '.' && start[1] == '.')
+ {
+ if (dest > curr + 1)
+ while ((--dest)[-1] != '/') ;
+ } else {
+ char lnkbuf[PATH_MAX + 1];
+ size_t len;
+ ssize_t n;
+
+ if (dest[-1] != '/')
+ *dest++ = '/';
+
+ if (dest + (end - start) > curr + PATH_MAX)
+ {
+ errno = ENAMETOOLONG;
+ return (char *)0;
+ }
+
+ dest = mempcpy(dest, start, end - start);
+ *dest = '\0';
+
+ if (deep-- < 0)
+ {
+ errno = ELOOP;
+ return (char *)0;
+ }
+
+ errno = 0;
+ if ((n = readlink(curr, lnkbuf, PATH_MAX)) < 0)
+ {
+ deep = MAXSYMLINKS;
+ if (errno == EINVAL)
+ continue; /* Not a symlink */
+ return (char *)0;
+ }
+ lnkbuf[n] = '\0'; /* Don't be fooled by readlink(2) */
+
+ len = strlen(end);
+ if ((n + len) > PATH_MAX)
+ {
+ errno = ENAMETOOLONG;
+ return (char *)0;
+ }
+
+ memmove(&tmpbuf[n], end, len + 1);
+ path = end = memcpy(tmpbuf, lnkbuf, n);
+
+ if (lnkbuf[0] == '/')
+ dest = curr + 1;
+ else if (dest > curr + 1)
+ while ((--dest)[-1] != '/') ;
+
+ }
+ }
+
+ if (dest > curr + 1 && dest[-1] == '/')
+ --dest;
+ *dest = '\0';
+
+ return curr;
+}