1
0
Fork 0
util-linux/lsfd-cmd/file.c
Daniel Baumann c36e531662
Adding upstream version 2.41.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
2025-06-21 11:26:35 +02:00

1052 lines
24 KiB
C

/*
* lsfd(1) - list file descriptors
*
* Copyright (C) 2021 Red Hat, Inc. All rights reserved.
* Written by Masatake YAMATO <yamato@redhat.com>
*
* Very generally based on lsof(8) by Victor A. Abell <abe@purdue.edu>
* It supports multiple OSes. lsfd specializes to Linux.
*
* 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 would 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
*/
#include <unistd.h>
#ifdef HAVE_LINUX_NSFS_H
# include <linux/nsfs.h>
# if defined(NS_GET_NSTYPE)
# define USE_NS_GET_API 1
# include <sys/ioctl.h>
# endif
#endif
#include <linux/sched.h>
#include <sys/shm.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <mqueue.h> /* mq_open */
#include "buffer.h"
#include "idcache.h"
#include "strutils.h"
#include "procfs.h"
#include "lsfd.h"
#include "pidfd.h"
#include "pidfd-utils.h"
#include "decode-file-flags.h"
static size_t pagesize;
/*
* Abstract file class
*
* This class is for filling columns that don't need "sb" member, the
* result of stat(2).
*/
#define does_file_has_fdinfo_alike(file) \
((file)->association >= 0 \
|| (file)->association == -ASSOC_SHM \
|| (file)->association == -ASSOC_MEM)
static struct idcache *username_cache;
static const char *assocstr[N_ASSOCS] = {
[ASSOC_CWD] = "cwd",
[ASSOC_EXE] = "exe",
/* "root" appears as user names, too.
* So we use "rtd" here instead of "root". */
[ASSOC_ROOT] = "rtd",
[ASSOC_NS_CGROUP] = "cgroup",
[ASSOC_NS_IPC] = "ipc",
[ASSOC_NS_MNT] = "mnt",
[ASSOC_NS_NET] = "net",
[ASSOC_NS_PID] = "pid",
[ASSOC_NS_PID4C] = "pid4c",
[ASSOC_NS_TIME] = "time",
[ASSOC_NS_TIME4C] = "time4c",
[ASSOC_NS_USER] = "user",
[ASSOC_NS_UTS] = "uts",
[ASSOC_MEM] = "mem",
[ASSOC_SHM] = "shm",
};
static void file_fill_flags_buf(struct ul_buffer *buf, int flags)
{
lsfd_decode_file_flags(buf, flags);
}
static uint64_t get_map_length(struct file *file)
{
uint64_t res = 0;
if (is_association(file, SHM) || is_association(file, MEM))
res = (file->map_end - file->map_start) / pagesize;
return res;
}
static void abst_class_initialize(void)
{
username_cache = new_idcache();
if (!username_cache)
err(EXIT_FAILURE, _("failed to allocate UID cache"));
}
static void abst_class_finalize(void)
{
free_idcache(username_cache);
}
static bool abst_fill_column(struct proc *proc,
struct file *file,
struct libscols_line *ln,
int column_id,
size_t column_index,
const char *uri __attribute__((__unused__)))
{
char *str = NULL;
switch(column_id) {
case COL_COMMAND:
if (proc->command
&& scols_line_set_data(ln, column_index, proc->command))
err(EXIT_FAILURE, _("failed to add output data"));
return true;
case COL_NAME:
case COL_KNAME:
if (file->name
&& scols_line_set_data(ln, column_index, file->name))
err(EXIT_FAILURE, _("failed to add output data"));
return true;
case COL_USER:
add_uid(username_cache, (int)proc->uid);
if (scols_line_set_data(ln, column_index,
get_id(username_cache,
(int)proc->uid)->name))
err(EXIT_FAILURE, _("failed to add output data"));
return true;
case COL_DEVTYPE:
if (scols_line_set_data(ln, column_index,
"nodev"))
err(EXIT_FAILURE, _("failed to add output data"));
return true;
case COL_FD:
if (!is_opened_file(file))
return false;
/* FALL THROUGH */
case COL_ASSOC:
if (is_opened_file(file))
xasprintf(&str, "%d", file->association);
else {
int assoc = file->association * -1;
if (assoc >= N_ASSOCS)
return false; /* INTERNAL ERROR */
str = xstrdup(assocstr[assoc]);
}
break;
case COL_PID:
xasprintf(&str, "%d", (int)proc->leader->pid);
break;
case COL_TID:
xasprintf(&str, "%d", (int)proc->pid);
break;
case COL_UID:
xasprintf(&str, "%d", (int)proc->uid);
break;
case COL_KTHREAD:
xasprintf(&str, "%u", proc->kthread);
break;
case COL_MODE:
xasprintf(&str, "???");
break;
case COL_XMODE: {
char r, w, x;
char D = '?';
char L = file->locked_write? 'L'
:file->locked_read? 'l'
: '-';
char m = file->multiplexed? 'm': '-';
r = w = x = '?';
xasprintf(&str, "%c%c%c%c%c%c", r, w, x, D, L, m);
break;
}
case COL_POS:
xasprintf(&str, "%" PRIu64,
(does_file_has_fdinfo_alike(file))? file->pos: 0);
break;
case COL_FLAGS: {
struct ul_buffer buf = UL_INIT_BUFFER;
if (!is_opened_file(file))
return true;
if (file->sys_flags == 0)
return true;
file_fill_flags_buf(&buf, file->sys_flags);
if (ul_buffer_is_empty(&buf))
return true;
str = ul_buffer_get_data(&buf, NULL, NULL);
break;
}
case COL_MAPLEN:
if (!is_mapped_file(file))
return true;
xasprintf(&str, "%ju", (uintmax_t)get_map_length(file));
break;
default:
return false;
}
if (!str)
err(EXIT_FAILURE, _("failed to add output data"));
if (scols_line_refer_data(ln, column_index, str))
err(EXIT_FAILURE, _("failed to add output data"));
return true;
}
const struct file_class abst_class = {
.super = NULL,
.size = sizeof(struct file),
.initialize_class = abst_class_initialize,
.finalize_class = abst_class_finalize,
.fill_column = abst_fill_column,
};
/*
* Error classes
*/
/* get_errno_name() --- the private replacement of strerrorname_np(3).
* Some platforms don't have strerrorname_np.
*
* Mainly copied from misc-utils/enosys.c.
*/
struct errno_s {
const char *const name;
long number;
};
static const struct errno_s errnos[] = {
#define UL_ERRNO(name, nr) { name, nr },
#include "errnos.h"
#undef UL_ERRNO
};
static const char *get_errno_name(int ern)
{
for (size_t i = 0; i < ARRAY_SIZE(errnos); i ++) {
if (errnos[i].number == ern)
return errnos[i].name;
}
return NULL;
}
static bool error_fill_column(struct proc *proc __attribute__((__unused__)),
struct file *file __attribute__((__unused__)),
struct libscols_line *ln,
int column_id,
size_t column_index,
const char *uri __attribute__((__unused__)))
{
char *str = NULL;
const char *ename;
switch(column_id) {
case COL_TYPE:
if (scols_line_set_data(ln, column_index, "ERROR"))
err(EXIT_FAILURE, _("failed to add output data"));
return true;
case COL_SOURCE:
ename = get_errno_name(file->error.number);
if (ename)
xasprintf(&str, "%s:%s",
file->error.syscall, ename);
else
xasprintf(&str, "%s:unknown(%d)",
file->error.syscall, file->error.number);
if (scols_line_refer_data(ln, column_index, str))
err(EXIT_FAILURE, _("failed to add output data"));
return true;
default:
return false;
}
}
static const struct file_class error_class = {
.super = &abst_class,
.size = sizeof(struct file),
.fill_column = error_fill_column,
};
static void init_error_content(struct file *file)
{
file->is_error = 1;
}
static bool readlink_error_fill_column(struct proc *proc __attribute__((__unused__)),
struct file *file __attribute__((__unused__)),
struct libscols_line *ln __attribute__((__unused__)),
int column_id,
size_t column_index __attribute__((__unused__)),
const char *uri __attribute__((__unused__)))
{
switch(column_id) {
case COL_NAME:
case COL_KNAME:
return true;
default:
return false;
}
}
const struct file_class readlink_error_class = {
.super = &error_class,
.size = sizeof(struct file),
.initialize_content = init_error_content,
.fill_column = readlink_error_fill_column,
};
const struct file_class stat_error_class = {
.super = &error_class,
.size = sizeof(struct file),
.initialize_content = init_error_content,
};
/*
* Concrete file class
*/
static const char *strftype(mode_t ftype)
{
switch (ftype) {
case S_IFBLK:
return "BLK";
case S_IFCHR:
return "CHR";
case S_IFDIR:
return "DIR";
case S_IFIFO:
return "FIFO";
case S_IFLNK:
return "LINK";
case S_IFREG:
return "REG";
case S_IFSOCK:
return "SOCK";
default:
return "UNKN";
}
}
void decode_source(char *buf, size_t bufsize,
unsigned int dev_major, unsigned int dev_minor,
enum decode_source_level level)
{
if (bufsize == 0)
return;
buf[0] = '\0';
if (level & DECODE_SOURCE_FILESYS_BIT) {
if (dev_major == 0) {
const char *filesystem = get_nodev_filesystem(dev_minor);
if (filesystem) {
xstrncpy(buf, filesystem, bufsize);
return;
}
}
}
if (level & DECODE_SOURCE_PARTITION_BIT) {
dev_t dev = makedev(dev_major, dev_minor);
const char *partition = get_partition(dev);
if (partition) {
xstrncpy(buf, partition, bufsize);
return;
}
}
if (level & DECODE_SOURCE_MAJMIN_BIT)
snprintf(buf, bufsize, "%u:%u",
dev_major,
dev_minor);
}
static char *strnrstr(const char *haystack, const char *needle, size_t needle_len)
{
char *last = strstr(haystack, needle);
if (last == NULL)
return NULL;
do {
char *current = strstr(last + needle_len, needle);
if (current == NULL)
return last;
last = current;
} while (1);
}
static bool file_fill_column(struct proc *proc __attribute__((__unused__)),
struct file *file,
struct libscols_line *ln,
int column_id,
size_t column_index,
const char *uri __attribute__((__unused__)))
{
char *str = NULL;
mode_t ftype;
char buf[BUFSIZ];
switch(column_id) {
case COL_NAME:
if (file->name && file->stat.st_nlink == 0) {
char *d = strnrstr(file->name, "(deleted)",
sizeof("(deleted)") - 1);
if (d) {
int r;
*d = '\0';
r = scols_line_set_data(ln, column_index, file->name);
*d = '(';
if (r)
err(EXIT_FAILURE, _("failed to add output data"));
if (uri) {
struct libscols_cell *ce = scols_line_get_cell(ln, column_index);
if (ce)
scols_cell_disable_uri(ce, 1);
}
return true;
}
}
/* FALL THROUGH */
case COL_KNAME:
if (file->name
&& scols_line_set_data(ln, column_index, file->name))
err(EXIT_FAILURE, _("failed to add output data"));
ftype = file->stat.st_mode & S_IFMT;
if (uri && (!file->name || *file->name != '/'
|| (ftype != S_IFREG && ftype != S_IFDIR)
|| file->stat.st_nlink == 0)) {
struct libscols_cell *ce = scols_line_get_cell(ln, column_index);
if (ce)
scols_cell_disable_uri(ce, 1);
}
return true;
case COL_STTYPE:
case COL_TYPE:
ftype = file->stat.st_mode & S_IFMT;
if (scols_line_set_data(ln, column_index, strftype(ftype)))
err(EXIT_FAILURE, _("failed to add output data"));
return true;
case COL_INODE:
xasprintf(&str, "%llu", (unsigned long long)file->stat.st_ino);
break;
case COL_SOURCE:
decode_source(buf, sizeof(buf), major(file->stat.st_dev), minor(file->stat.st_dev),
DECODE_SOURCE_FILESYS);
str = xstrdup(buf);
break;
case COL_PARTITION:
decode_source(buf, sizeof(buf), major(file->stat.st_dev), minor(file->stat.st_dev),
DECODE_SOURCE_PARTITION);
str = xstrdup(buf);
break;
case COL_DEV:
case COL_MAJMIN:
decode_source(buf, sizeof(buf), major(file->stat.st_dev), minor(file->stat.st_dev),
DECODE_SOURCE_MAJMIN);
str = xstrdup(buf);
break;
case COL_RDEV:
xasprintf(&str, "%u:%u",
major(file->stat.st_rdev),
minor(file->stat.st_rdev));
break;
case COL_FUID:
xasprintf(&str, "%d", (int)file->stat.st_uid);
break;
case COL_SIZE:
xasprintf(&str, "%jd", (intmax_t)file->stat.st_size);
break;
case COL_NLINK:
xasprintf(&str, "%ju", (uintmax_t)file->stat.st_nlink);
break;
case COL_DELETED:
xasprintf(&str, "%d", file->stat.st_nlink == 0);
break;
case COL_MNT_ID:
xasprintf(&str, "%d", is_opened_file(file)? file->mnt_id: 0);
break;
case COL_MODE:
if (does_file_has_fdinfo_alike(file))
xasprintf(&str, "%c%c%c",
file->mode & S_IRUSR? 'r': '-',
file->mode & S_IWUSR? 'w': '-',
(is_mapped_file(file)
&& file->mode & S_IXUSR)? 'x': '-');
else
xasprintf(&str, "---");
break;
case COL_XMODE: {
char r, w, x;
char D = file->stat.st_nlink == 0? 'D': '-';
char L = file->locked_write? 'L'
:file->locked_read? 'l'
: '-';
char m = file->multiplexed? 'm': '-';
if (does_file_has_fdinfo_alike(file)) {
r = file->mode & S_IRUSR? 'r': '-';
w = file->mode & S_IWUSR? 'w': '-';
x = (is_mapped_file(file)
&& file->mode & S_IXUSR)? 'x': '-';
} else
r = w = x = '-';
xasprintf(&str, "%c%c%c%c%c%c", r, w, x, D, L, m);
break;
}
default:
return false;
}
if (!str)
err(EXIT_FAILURE, _("failed to add output data"));
if (scols_line_refer_data(ln, column_index, str))
err(EXIT_FAILURE, _("failed to add output data"));
return true;
}
enum lock_mode {
LOCK_NONE,
READ_LOCK,
WRITE_LOCK,
};
static unsigned int parse_lock_line(const char *line)
{
char mode[6] = {0};
/* Examples of lines:
----------------------------------------------------
1: FLOCK ADVISORY READ 2283292 fd:03:26219728 0 EOF
1: FLOCK ADVISORY WRITE 2283321 fd:03:26219728 0 EOF
1: POSIX ADVISORY READ 2283190 fd:03:26219728 0 0
1: POSIX ADVISORY WRITE 2283225 fd:03:26219728 0 0
1: OFDLCK ADVISORY READ -1 fd:03:26219728 0 0
1: OFDLCK ADVISORY WRITE -1 fd:03:26219728 0 0
1: LEASE ACTIVE WRITE 2328907 fd:03:26219472 0 EOF
1: LEASE ACTIVE READ 2326777 fd:03:26219472 0 EOF
---------------------------------------------------- */
if (sscanf(line, "%*d: %*s %*s %5s %*s", mode) != 1)
return LOCK_NONE;
if (strcmp(mode, "READ") == 0)
return READ_LOCK;
if (strcmp(mode, "WRITE") == 0)
return WRITE_LOCK;
return LOCK_NONE;
}
static int file_handle_fdinfo(struct file *file, const char *key, const char* value)
{
int rc;
if (strcmp(key, "pos") == 0) {
rc = ul_strtou64(value, &file->pos, 10);
} else if (strcmp(key, "flags") == 0) {
rc = ul_strtou32(value, &file->sys_flags, 8);
} else if (strcmp(key, "mnt_id") == 0) {
rc = ul_strtou32(value, &file->mnt_id, 10);
} else if (strcmp(key, "lock") == 0) {
switch (parse_lock_line(value)) {
case READ_LOCK:
file->locked_read = 1;
break;
case WRITE_LOCK:
file->locked_write = 1;
break;
}
rc = 1;
} else
return 0; /* ignore -- unknown item */
if (rc < 0)
return 0; /* ignore -- parse failed */
return 1; /* success */
}
static void file_free_content(struct file *file)
{
free(file->name);
}
static unsigned long get_minor_for_sysvipc(void)
{
int id;
void *start;
pid_t self = getpid();
struct path_cxt *pc = NULL;
struct stat sb;
unsigned long m = 0;
id = shmget(IPC_PRIVATE, pagesize, IPC_CREAT | 0600);
if (id == -1)
return 0;
start = shmat(id, NULL, SHM_RDONLY);
if (start == (void *) -1) {
shmctl(id, IPC_RMID, NULL);
return 0;
}
pc = ul_new_path(NULL);
if (!pc)
goto out;
if (procfs_process_init_path(pc, self) != 0)
goto out;
if (ul_path_statf(pc, &sb, 0, "map_files/%lx-%lx",
(long)start, (long)start + pagesize) < 0)
goto out;
m = minor(sb.st_dev);
out:
if (pc)
ul_unref_path(pc);
shmdt(start);
shmctl(id, IPC_RMID, NULL);
return m;
}
static unsigned long get_minor_for_mqueue(void)
{
mqd_t mq;
char mq_name[BUFSIZ];
struct mq_attr attr = {
.mq_maxmsg = 1,
.mq_msgsize = 1,
};
pid_t self = getpid();
struct stat sb;
snprintf(mq_name, sizeof(mq_name), "/.lsfd-mqueue-nodev-test:%d", self);
mq = mq_open(mq_name, O_CREAT|O_EXCL | O_RDONLY, S_IRUSR | S_IWUSR, &attr);
if (mq < 0)
return 0;
if (fstat((int)mq, &sb) < 0) {
mq_close(mq);
mq_unlink(mq_name);
return 0;
}
mq_close(mq);
mq_unlink(mq_name);
return minor(sb.st_dev);
}
static unsigned long get_minor_for_pidfs(void)
{
int fd = pidfd_open(getpid(), 0);
struct stat sb;
unsigned long ret = 0;
if (fd < 0)
return 0;
if (fstat(fd, &sb) == 0 && (sb.st_mode & S_IFMT) == S_IFREG)
ret = minor(sb.st_dev);
close(fd);
return ret;
}
static void file_class_initialize(void)
{
unsigned long m;
if (!pagesize)
pagesize = getpagesize();
m = get_minor_for_sysvipc();
if (m)
add_nodev(m, "tmpfs");
m = get_minor_for_mqueue();
if (m)
add_nodev(m, "mqueue");
m = get_minor_for_pidfs();
if (m)
add_nodev(m, "pidfs");
}
const struct file_class file_class = {
.super = &abst_class,
.size = sizeof(struct file),
.initialize_class = file_class_initialize,
.finalize_class = NULL,
.fill_column = file_fill_column,
.handle_fdinfo = file_handle_fdinfo,
.free_content = file_free_content,
};
/*
* Regular files on NSFS
*/
struct nsfs_file {
struct file file;
int clone_type;
};
static const char *get_ns_type_name(int clone_type)
{
switch (clone_type) {
#ifdef USE_NS_GET_API
case CLONE_NEWNS:
return "mnt";
case CLONE_NEWCGROUP:
return "cgroup";
case CLONE_NEWUTS:
return "uts";
case CLONE_NEWIPC:
return "ipc";
case CLONE_NEWUSER:
return "user";
case CLONE_NEWPID:
return "pid";
case CLONE_NEWNET:
return "net";
#ifdef CLONE_NEWTIME
case CLONE_NEWTIME:
return "time";
#endif /* CLONE_NEWTIME */
#endif /* USE_NS_GET_API */
default:
return "unknown";
}
}
static void init_nsfs_file_content(struct file *file)
{
struct nsfs_file *nsfs_file = (struct nsfs_file *)file;
nsfs_file->clone_type = -1;
#ifdef USE_NS_GET_API
char *proc_fname = NULL;
int ns_fd;
int ns_type;
if (is_association(file, NS_CGROUP))
nsfs_file->clone_type = CLONE_NEWCGROUP;
else if (is_association(file, NS_IPC))
nsfs_file->clone_type = CLONE_NEWIPC;
else if (is_association(file, NS_MNT))
nsfs_file->clone_type = CLONE_NEWNS;
else if (is_association(file, NS_NET))
nsfs_file->clone_type = CLONE_NEWNET;
else if (is_association(file, NS_PID)
|| is_association(file, NS_PID4C))
nsfs_file->clone_type = CLONE_NEWPID;
#ifdef CLONE_NEWTIME
else if (is_association(file, NS_TIME)
|| is_association(file, NS_TIME4C))
nsfs_file->clone_type = CLONE_NEWTIME;
#endif
else if (is_association(file, NS_USER))
nsfs_file->clone_type = CLONE_NEWUSER;
else if (is_association(file, NS_UTS))
nsfs_file->clone_type = CLONE_NEWUTS;
if (nsfs_file->clone_type != -1)
return;
if (!is_opened_file(file))
return;
if (!file->name)
return;
xasprintf(&proc_fname, "/proc/%d/fd/%d",
file->proc->pid, file->association);
ns_fd = open(proc_fname, O_RDONLY);
free(proc_fname);
if (ns_fd < 0)
return;
ns_type = ioctl(ns_fd, NS_GET_NSTYPE);
close(ns_fd);
if (ns_type < 0)
return;
nsfs_file->clone_type = ns_type;
#endif /* USE_NS_GET_API */
}
static bool nsfs_file_fill_column(struct proc *proc __attribute__((__unused__)),
struct file *file,
struct libscols_line *ln,
int column_id,
size_t column_index,
const char *uri __attribute__((__unused__)))
{
struct nsfs_file *nsfs_file = (struct nsfs_file *)file;
char *name = NULL;
if (nsfs_file->clone_type == -1)
return false;
switch (column_id) {
case COL_NS_NAME:
xasprintf(&name, "%s:[%llu]",
get_ns_type_name(nsfs_file->clone_type),
(unsigned long long)file->stat.st_ino);
break;
case COL_NS_TYPE:
if (scols_line_set_data(ln, column_index,
get_ns_type_name(nsfs_file->clone_type)))
err(EXIT_FAILURE, _("failed to add output data"));
return true;
default:
return false;
}
if (name && scols_line_refer_data(ln, column_index, name))
err(EXIT_FAILURE, _("failed to add output data"));
return true;
}
const struct file_class nsfs_file_class = {
.super = &file_class,
.size = sizeof(struct nsfs_file),
.initialize_class = NULL,
.finalize_class = NULL,
.initialize_content = init_nsfs_file_content,
.free_content = NULL,
.fill_column = nsfs_file_fill_column,
.handle_fdinfo = NULL,
};
/*
* POSIX Mqueue
*/
struct mqueue_file {
struct file file;
struct ipc_endpoint endpoint;
};
struct mqueue_file_ipc {
struct ipc ipc;
ino_t ino;
};
bool is_mqueue_dev(dev_t dev)
{
const char *fs = get_nodev_filesystem(minor(dev));
if (fs && (strcmp (fs, "mqueue") == 0))
return true;
return false;
}
static inline char *mqueue_file_xstrendpoint(struct file *file)
{
char *str = NULL;
xasprintf(&str, "%d,%s,%d%c%c",
file->proc->pid, file->proc->command, file->association,
(file->mode & S_IRUSR)? 'r': '-',
(file->mode & S_IWUSR)? 'w': '-');
return str;
}
static bool mqueue_file_fill_column(struct proc *proc __attribute__((__unused__)),
struct file *file __attribute__((__unused__)),
struct libscols_line *ln,
int column_id,
size_t column_index,
const char *uri __attribute__((__unused__)))
{
switch (column_id) {
case COL_TYPE:
if (scols_line_set_data(ln, column_index, "mqueue"))
err(EXIT_FAILURE, _("failed to add output data"));
return true;
case COL_ENDPOINTS: {
char *str = NULL;
struct mqueue_file *this = (struct mqueue_file *)file;
struct list_head *e;
foreach_endpoint(e, this->endpoint) {
char *estr;
struct mqueue_file *other = list_entry(e, struct mqueue_file,
endpoint.endpoints);
if (this == other)
continue;
if (str)
xstrputc(&str, '\n');
estr = mqueue_file_xstrendpoint(&other->file);
xstrappend(&str, estr);
free(estr);
}
if (!str)
return false;
if (scols_line_refer_data(ln, column_index, str))
err(EXIT_FAILURE, _("failed to add output data"));
return true;
}
default:
return false;
}
}
static unsigned int mqueue_file_get_hash(struct file *file)
{
return (unsigned int)(file->stat.st_ino % UINT_MAX);
}
static bool mqueue_file_is_suitable_ipc(struct ipc *ipc, struct file *file)
{
return ((struct mqueue_file_ipc *)ipc)->ino == file->stat.st_ino;
}
static const struct ipc_class *mqueue_file_get_ipc_class(struct file *file __attribute__((__unused__)))
{
static const struct ipc_class mqueue_file_ipc_class = {
.size = sizeof(struct mqueue_file_ipc),
.get_hash = mqueue_file_get_hash,
.is_suitable_ipc = mqueue_file_is_suitable_ipc,
};
return &mqueue_file_ipc_class;
}
static void init_mqueue_file_content(struct file *file)
{
struct mqueue_file *mqueue_file = (struct mqueue_file *)file;
struct ipc *ipc;
unsigned int hash;
init_endpoint(&mqueue_file->endpoint);
ipc = get_ipc(file);
if (ipc)
goto link;
ipc = new_ipc(mqueue_file_get_ipc_class(file));
((struct mqueue_file_ipc *)ipc)->ino = file->stat.st_ino;
hash = mqueue_file_get_hash(file);
add_ipc(ipc, hash);
link:
add_endpoint(&mqueue_file->endpoint, ipc);
}
const struct file_class mqueue_file_class = {
.super = &file_class,
.size = sizeof(struct mqueue_file),
.initialize_content = init_mqueue_file_content,
.fill_column = mqueue_file_fill_column,
.get_ipc_class = mqueue_file_get_ipc_class,
};
struct pidfs_file {
struct file file;
struct pidfd_data data;
};
static void init_pidfs_file_content(struct file *file)
{
struct pidfs_file *pidfs_file = (struct pidfs_file *)file;
memset(&pidfs_file->data, 0, sizeof(pidfs_file->data));
}
static int pidfs_file_handle_fdinfo(struct file *file, const char *key, const char *value)
{
struct pidfs_file *pidfs_file = (struct pidfs_file *)file;
return pidfd_handle_fdinfo(&pidfs_file->data, key, value);
}
static void pidfs_file_free_content(struct file *file)
{
struct pidfs_file *pidfs_file = (struct pidfs_file *)file;
pidfd_free(&pidfs_file->data);
}
static bool pidfs_file_fill_column(struct proc *proc __attribute__((__unused__)),
struct file *file,
struct libscols_line *ln,
int column_id,
size_t column_index,
const char *uri __attribute__((__unused__)))
{
struct pidfs_file *pidfs_file = (struct pidfs_file *)file;
char *buf = NULL;
switch(column_id) {
case COL_TYPE:
if (scols_line_set_data(ln, column_index, "pidfd"))
err(EXIT_FAILURE, _("failed to add output data"));
return true;
case COL_NAME:
buf = pidfd_get_name(&pidfs_file->data);
break;
default:
if (!pidfd_fill_column(&pidfs_file->data, column_id, &buf))
return false;
}
if (buf &&
scols_line_refer_data(ln, column_index, buf))
err(EXIT_FAILURE, _("failed to add output data"));
return true;
}
const struct file_class pidfs_file_class = {
.super = &file_class,
.size = sizeof(struct pidfs_file),
.initialize_content = init_pidfs_file_content,
.handle_fdinfo = pidfs_file_handle_fdinfo,
.fill_column = pidfs_file_fill_column,
.free_content = pidfs_file_free_content,
};
bool is_pidfs_dev(dev_t dev)
{
const char *fs = get_nodev_filesystem(minor(dev));
if (fs && (strcmp (fs, "pidfs") == 0))
return true;
return false;
}