util-linux/misc-utils/lsfd-file.c

401 lines
9.0 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>
#include "xalloc.h"
#include "nls.h"
#include "buffer.h"
#include "idcache.h"
#include "strutils.h"
#include "libsmartcols.h"
#include "lsfd.h"
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 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";
}
}
/* See /usr/include/asm-generic/fcntl.h */
static void file_fill_flags_buf(struct ul_buffer *buf, int flags)
{
#define SET_FLAG_FULL(L,s) \
do { \
if (flags & (L)) { \
if (!ul_buffer_is_empty(buf)) \
ul_buffer_append_data(buf, ",", 1); \
ul_buffer_append_string(buf, #s); \
} \
} while (0)
#define SET_FLAG(L,s) SET_FLAG_FULL(O_##L,s)
#ifdef O_WRONLY
SET_FLAG(WRONLY,wronly);
#endif
#ifdef O_RDWR
SET_FLAG(RDWR,rdwr);
#endif
#ifdef O_CREAT
SET_FLAG(CREAT,creat);
#endif
#ifdef O_EXCL
SET_FLAG(EXCL,excl);
#endif
#ifdef O_NOCTTY
SET_FLAG(NOCTTY,noctty);
#endif
#ifdef O_APPEND
SET_FLAG(APPEND,append);
#endif
#ifdef O_NONBLOCK
SET_FLAG(NONBLOCK,nonblock);
#endif
#ifdef O_DSYNC
SET_FLAG(DSYNC,dsync);
#endif
#ifdef O_FASYNC
SET_FLAG(FASYNC,fasync);
#endif
#ifdef O_DIRECT
SET_FLAG(DIRECT,direct);
#endif
#ifdef O_LARGEFILE
SET_FLAG(LARGEFILE,largefile);
#endif
#ifdef O_DIRECTORY
SET_FLAG(DIRECTORY,directory);
#endif
#ifdef O_FOLLOW
SET_FLAG(FOLLOW,follow);
#endif
#ifdef O_NOATIME
SET_FLAG(NOATIME,noatime);
#endif
#ifdef O_CLOEXEC
SET_FLAG(CLOEXEC,cloexec);
#endif
#ifdef __O_SYNC
SET_FLAG_FULL(__O_SYNC,_sync);
#endif
#ifdef O_PATH
SET_FLAG(PATH,path);
#endif
#ifdef __O_TMPFILE
SET_FLAG_FULL(__O_TMPFILE,_tmpfile);
#endif
}
#define does_file_has_fdinfo_alike(file) \
((file)->association >= 0 \
|| (file)->association == -ASSOC_SHM \
|| (file)->association == -ASSOC_MEM)
static uint64_t get_map_length(struct file *file)
{
uint64_t res = 0;
if (is_association(file, SHM) || is_association(file, MEM)) {
static size_t pagesize = 0;
if (!pagesize)
pagesize = getpagesize();
res = (file->map_end - file->map_start) / pagesize;
}
return res;
}
static bool file_fill_column(struct proc *proc,
struct file *file,
struct libscols_line *ln,
int column_id,
size_t column_index)
{
char *str = NULL;
mode_t ftype;
const char *partition;
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:
if (file->name
&& scols_line_set_data(ln, column_index, file->name))
err(EXIT_FAILURE, _("failed to add output data"));
return true;
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_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_OWNER:
add_uid(username_cache, (int)file->stat.st_uid);
if (scols_line_set_data(ln, column_index,
get_id(username_cache,
(int)file->stat.st_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 (file->association < 0)
return false;
/* FALL THROUGH */
case COL_ASSOC:
if (file->association >= 0)
xasprintf(&str, "%d", file->association);
else {
int assoc = file->association * -1;
if (assoc >= N_ASSOCS)
return false; /* INTERNAL ERROR */
xasprintf(&str, "%s", assocstr[assoc]);
}
break;
case COL_INODE:
xasprintf(&str, "%llu", (unsigned long long)file->stat.st_ino);
break;
case COL_SOURCE:
if (major(file->stat.st_dev) == 0) {
const char *filesystem = get_nodev_filesystem(minor(file->stat.st_dev));
if (filesystem) {
xasprintf(&str, "%s", filesystem);
break;
}
}
/* FALL THROUGH */
case COL_PARTITION:
partition = get_partition(file->stat.st_dev);
if (partition) {
str = strdup(partition);
break;
}
/* FALL THROUGH */
case COL_DEV:
case COL_MAJMIN:
xasprintf(&str, "%u:%u",
major(file->stat.st_dev),
minor(file->stat.st_dev));
break;
case COL_RDEV:
xasprintf(&str, "%u:%u",
major(file->stat.st_rdev),
minor(file->stat.st_rdev));
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_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_KTHREAD:
xasprintf(&str, "%u", proc->kthread);
break;
case COL_MNT_ID:
xasprintf(&str, "%d", file->association < 0? 0: file->mnt_id);
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': '-',
((file->association == -ASSOC_SHM
|| file->association == -ASSOC_MEM)
&& file->mode & S_IXUSR)? 'x': '-');
else
xasprintf(&str, "---");
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 (file->association < 0)
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 (file->association != -ASSOC_SHM
&& file->association != -ASSOC_MEM)
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;
}
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
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 void file_class_initialize(void)
{
username_cache = new_idcache();
if (!username_cache)
err(EXIT_FAILURE, _("failed to allocate UID cache"));
}
static void file_class_finalize(void)
{
free_idcache(username_cache);
}
const struct file_class file_class = {
.super = NULL,
.size = sizeof(struct file),
.initialize_class = file_class_initialize,
.finalize_class = file_class_finalize,
.fill_column = file_fill_column,
.handle_fdinfo = file_handle_fdinfo,
.free_content = file_free_content,
};