summaryrefslogtreecommitdiffstats
path: root/misc-utils/lsfd-unkn.c
blob: 087e31dad2f287eb39a143f6cfe04b4785fdd3e8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
/*
 * lsfd-unkn.c - handle associations opening unknown objects
 *
 * Copyright (C) 2021 Red Hat, Inc. All rights reserved.
 * Written by Masatake YAMATO <yamato@redhat.com>
 *
 * 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 "xalloc.h"
#include "nls.h"
#include "libsmartcols.h"

#include "lsfd.h"

struct unkn {
	struct file file;
	const struct anon_ops *anon_ops;
	void *anon_data;
};

struct anon_ops {
	const char *class;
	char * (*get_name)(struct unkn *);
	/* Return true is handled the column. */
	bool (*fill_column)(struct proc *,
			    struct unkn *,
			    struct libscols_line *,
			    int,
			    size_t,
			    char **str);
	void (*init)(struct unkn *);
	void (*free)(struct unkn *);
	int (*handle_fdinfo)(struct unkn *, const char *, const char *);
};

static const struct anon_ops anon_generic_ops;
static const struct anon_ops anon_pidfd_ops;

static char * anon_get_class(struct unkn *unkn)
{
	char *name;

	if (unkn->anon_ops->class)
		return xstrdup(unkn->anon_ops->class);

	/* See unkn_init_content() */
	name = ((struct file *)unkn)->name + 11;
	/* Does it have the form anon_inode:[class]? */
	if (*name == '[') {
		size_t len = strlen(name + 1);
		if (*(name + 1 + len - 1) == ']')
			return strndup(name + 1, len - 1);
	}

	return xstrdup(name);
}

static bool unkn_fill_column(struct proc *proc,
			     struct file *file,
			     struct libscols_line *ln,
			     int column_id,
			     size_t column_index)
{
	char *str = NULL;
	struct unkn *unkn = (struct unkn *)file;

	switch(column_id) {
	case COL_NAME:
		if (unkn->anon_ops && unkn->anon_ops->get_name) {
			str = unkn->anon_ops->get_name(unkn);
			if (str)
				break;
		}
		return false;
	case COL_TYPE:
		if (!unkn->anon_ops)
			return false;
		/* FALL THROUGH */
	case COL_AINODECLASS:
		if (unkn->anon_ops) {
			str = anon_get_class(unkn);
			break;
		}
		return false;
	case COL_SOURCE:
		if (unkn->anon_ops) {
			str = xstrdup("anon_inodefs");
			break;
		}
		return false;
	default:
		if (unkn->anon_ops && unkn->anon_ops->fill_column) {
			if (unkn->anon_ops->fill_column(proc, unkn, ln,
							column_id, column_index, &str))
				break;
		}
		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 void unkn_init_content(struct file *file)
{
	struct unkn *unkn = (struct unkn *)file;

	assert(file);
	unkn->anon_ops = NULL;
	unkn->anon_data = NULL;

	if (major(file->stat.st_dev) == 0
	    && strncmp(file->name, "anon_inode:", 11) == 0) {
		const char *rest = file->name + 11;

		if (strncmp(rest, "[pidfd]", 7) == 0)
			unkn->anon_ops = &anon_pidfd_ops;
		else
			unkn->anon_ops = &anon_generic_ops;

		if (unkn->anon_ops->init)
			unkn->anon_ops->init(unkn);
	}
}

static void unkn_content_free(struct file *file)
{
	struct unkn *unkn = (struct unkn *)file;

	assert(file);
	if (unkn->anon_ops && unkn->anon_ops->free)
		unkn->anon_ops->free((struct unkn *)file);
}

static int unkn_handle_fdinfo(struct file *file, const char *key, const char *value)
{
	struct unkn *unkn = (struct unkn *)file;

	assert(file);
	if (unkn->anon_ops && unkn->anon_ops->handle_fdinfo)
		return unkn->anon_ops->handle_fdinfo(unkn, key, value);
	return 0;		/* Should be handled in parents */
}

/*
 * pidfd
 */
struct anon_pidfd_data {
	pid_t pid;
	char *nspid;
};

static char *anon_pidfd_get_name(struct unkn *unkn)
{
	char *str = NULL;
	struct anon_pidfd_data *data = (struct anon_pidfd_data *)unkn->anon_data;

	char *comm = NULL;
	struct proc *proc = get_proc(data->pid);
	if (proc)
		comm = proc->command;

	xasprintf(&str, "pid=%d comm=%s nspid=%s",
		  data->pid,
		  comm? comm: "",
		  data->nspid? data->nspid: "");
	return str;
}

static void anon_pidfd_init(struct unkn *unkn)
{
	unkn->anon_data = xcalloc(1, sizeof(struct anon_pidfd_data));
}

static void anon_pidfd_free(struct unkn *unkn)
{
	struct anon_pidfd_data *data = (struct anon_pidfd_data *)unkn->anon_data;

	if (data->nspid)
		free(data->nspid);
	free(data);
}

static int anon_pidfd_handle_fdinfo(struct unkn *unkn, const char *key, const char *value)
{
	if (strcmp(key, "Pid") == 0) {
		uint64_t pid;

		int rc = ul_strtou64(value, &pid, 10);
		if (rc < 0)
			return 0; /* ignore -- parse failed */
		((struct anon_pidfd_data *)unkn->anon_data)->pid = (pid_t)pid;
		return 1;
	}
	else if (strcmp(key, "NSpid") == 0) {
		((struct anon_pidfd_data *)unkn->anon_data)->nspid = xstrdup(value);
		return 1;

	}
	return 0;
}

static bool anon_pidfd_fill_column(struct proc *proc  __attribute__((__unused__)),
				   struct unkn *unkn,
				   struct libscols_line *ln __attribute__((__unused__)),
				   int column_id,
				   size_t column_index __attribute__((__unused__)),
				   char **str)
{
	struct anon_pidfd_data *data = (struct anon_pidfd_data *)unkn->anon_data;

	switch(column_id) {
	case COL_PIDFD_COMM: {
		struct proc *pidfd_proc = get_proc(data->pid);
		char *pidfd_comm = NULL;
		if (pidfd_proc)
			pidfd_comm = pidfd_proc->command;
		if (pidfd_comm) {
			*str = xstrdup(pidfd_comm);
			return true;
		}
		break;
	}
	case COL_PIDFD_NSPID:
		if (data->nspid) {
			*str = xstrdup(data->nspid);
			return true;
		}
		break;
	case COL_PIDFD_PID:
		xasprintf(str, "%d", (int)data->pid);
		return true;
	}

	return false;
}

static const struct anon_ops anon_pidfd_ops = {
	.class = "pidfd",
	.get_name = anon_pidfd_get_name,
	.fill_column = anon_pidfd_fill_column,
	.init = anon_pidfd_init,
	.free = anon_pidfd_free,
	.handle_fdinfo = anon_pidfd_handle_fdinfo,
};

/*
 * generic (fallback implementation)
 */
static const struct anon_ops anon_generic_ops = {
	.class = NULL,
	.get_name = NULL,
	.fill_column = NULL,
	.init = NULL,
	.free = NULL,
	.handle_fdinfo = NULL,
};

const struct file_class unkn_class = {
	.super = &file_class,
	.size = sizeof(struct unkn),
	.fill_column = unkn_fill_column,
	.initialize_content = unkn_init_content,
	.free_content = unkn_content_free,
	.handle_fdinfo = unkn_handle_fdinfo,
};