1 /*
2 * lsfd-unkn.c - handle associations opening unknown objects
3 *
4 * Copyright (C) 2021 Red Hat, Inc. All rights reserved.
5 * Written by Masatake YAMATO <yamato@redhat.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it would be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software Foundation,
19 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22 #include "xalloc.h"
23 #include "nls.h"
24 #include "libsmartcols.h"
25
26 #include "lsfd.h"
27
28 struct unkn {
29 struct file file;
30 const struct anon_ops *anon_ops;
31 void *anon_data;
32 };
33
34 struct anon_ops {
35 const char *class;
36 char * (*get_name)(struct unkn *);
37 /* Return true is handled the column. */
38 bool (*fill_column)(struct proc *,
39 struct unkn *,
40 struct libscols_line *,
41 int,
42 size_t,
43 char **str);
44 void (*init)(struct unkn *);
45 void (*free)(struct unkn *);
46 int (*handle_fdinfo)(struct unkn *, const char *, const char *);
47 };
48
49 static const struct anon_ops anon_generic_ops;
50 static const struct anon_ops anon_pidfd_ops;
51
52 static char * anon_get_class(struct unkn *unkn)
53 {
54 char *name;
55
56 if (unkn->anon_ops->class)
57 return xstrdup(unkn->anon_ops->class);
58
59 /* See unkn_init_content() */
60 name = ((struct file *)unkn)->name + 11;
61 /* Does it have the form anon_inode:[class]? */
62 if (*name == '[') {
63 size_t len = strlen(name + 1);
64 if (*(name + 1 + len - 1) == ']')
65 return strndup(name + 1, len - 1);
66 }
67
68 return xstrdup(name);
69 }
70
71 static bool unkn_fill_column(struct proc *proc,
72 struct file *file,
73 struct libscols_line *ln,
74 int column_id,
75 size_t column_index)
76 {
77 char *str = NULL;
78 struct unkn *unkn = (struct unkn *)file;
79
80 switch(column_id) {
81 case COL_NAME:
82 if (unkn->anon_ops && unkn->anon_ops->get_name) {
83 str = unkn->anon_ops->get_name(unkn);
84 if (str)
85 break;
86 }
87 return false;
88 case COL_TYPE:
89 if (!unkn->anon_ops)
90 return false;
91 /* FALL THROUGH */
92 case COL_AINODECLASS:
93 if (unkn->anon_ops) {
94 str = anon_get_class(unkn);
95 break;
96 }
97 return false;
98 case COL_SOURCE:
99 if (unkn->anon_ops) {
100 str = xstrdup("anon_inodefs");
101 break;
102 }
103 return false;
104 default:
105 if (unkn->anon_ops && unkn->anon_ops->fill_column) {
106 if (unkn->anon_ops->fill_column(proc, unkn, ln,
107 column_id, column_index, &str))
108 break;
109 }
110 return false;
111 }
112
113 if (!str)
114 err(EXIT_FAILURE, _("failed to add output data"));
115 if (scols_line_refer_data(ln, column_index, str))
116 err(EXIT_FAILURE, _("failed to add output data"));
117 return true;
118 }
119
120 static void unkn_init_content(struct file *file)
121 {
122 struct unkn *unkn = (struct unkn *)file;
123
124 assert(file);
125 unkn->anon_ops = NULL;
126 unkn->anon_data = NULL;
127
128 if (major(file->stat.st_dev) == 0
129 && strncmp(file->name, "anon_inode:", 11) == 0) {
130 const char *rest = file->name + 11;
131
132 if (strncmp(rest, "[pidfd]", 7) == 0)
133 unkn->anon_ops = &anon_pidfd_ops;
134 else
135 unkn->anon_ops = &anon_generic_ops;
136
137 if (unkn->anon_ops->init)
138 unkn->anon_ops->init(unkn);
139 }
140 }
141
142 static void unkn_content_free(struct file *file)
143 {
144 struct unkn *unkn = (struct unkn *)file;
145
146 assert(file);
147 if (unkn->anon_ops && unkn->anon_ops->free)
148 unkn->anon_ops->free((struct unkn *)file);
149 }
150
151 static int unkn_handle_fdinfo(struct file *file, const char *key, const char *value)
152 {
153 struct unkn *unkn = (struct unkn *)file;
154
155 assert(file);
156 if (unkn->anon_ops && unkn->anon_ops->handle_fdinfo)
157 return unkn->anon_ops->handle_fdinfo(unkn, key, value);
158 return 0; /* Should be handled in parents */
159 }
160
161 /*
162 * pidfd
163 */
164 struct anon_pidfd_data {
165 pid_t pid;
166 char *nspid;
167 };
168
169 static char *anon_pidfd_get_name(struct unkn *unkn)
170 {
171 char *str = NULL;
172 struct anon_pidfd_data *data = (struct anon_pidfd_data *)unkn->anon_data;
173
174 char *comm = NULL;
175 struct proc *proc = get_proc(data->pid);
176 if (proc)
177 comm = proc->command;
178
179 xasprintf(&str, "pid=%d comm=%s nspid=%s",
180 data->pid,
181 comm? comm: "",
182 data->nspid? data->nspid: "");
183 return str;
184 }
185
186 static void anon_pidfd_init(struct unkn *unkn)
187 {
188 unkn->anon_data = xcalloc(1, sizeof(struct anon_pidfd_data));
189 }
190
191 static void anon_pidfd_free(struct unkn *unkn)
192 {
193 struct anon_pidfd_data *data = (struct anon_pidfd_data *)unkn->anon_data;
194
195 if (data->nspid)
196 free(data->nspid);
197 free(data);
198 }
199
200 static int anon_pidfd_handle_fdinfo(struct unkn *unkn, const char *key, const char *value)
201 {
202 if (strcmp(key, "Pid") == 0) {
203 uint64_t pid;
204
205 int rc = ul_strtou64(value, &pid, 10);
206 if (rc < 0)
207 return 0; /* ignore -- parse failed */
208 ((struct anon_pidfd_data *)unkn->anon_data)->pid = (pid_t)pid;
209 return 1;
210 }
211 else if (strcmp(key, "NSpid") == 0) {
212 ((struct anon_pidfd_data *)unkn->anon_data)->nspid = xstrdup(value);
213 return 1;
214
215 }
216 return 0;
217 }
218
219 static bool anon_pidfd_fill_column(struct proc *proc __attribute__((__unused__)),
220 struct unkn *unkn,
221 struct libscols_line *ln __attribute__((__unused__)),
222 int column_id,
223 size_t column_index __attribute__((__unused__)),
224 char **str)
225 {
226 struct anon_pidfd_data *data = (struct anon_pidfd_data *)unkn->anon_data;
227
228 switch(column_id) {
229 case COL_PIDFD_COMM: {
230 struct proc *pidfd_proc = get_proc(data->pid);
231 char *pidfd_comm = NULL;
232 if (pidfd_proc)
233 pidfd_comm = pidfd_proc->command;
234 if (pidfd_comm) {
235 *str = xstrdup(pidfd_comm);
236 return true;
237 }
238 break;
239 }
240 case COL_PIDFD_NSPID:
241 if (data->nspid) {
242 *str = xstrdup(data->nspid);
243 return true;
244 }
245 break;
246 case COL_PIDFD_PID:
247 xasprintf(str, "%d", (int)data->pid);
248 return true;
249 }
250
251 return false;
252 }
253
254 static const struct anon_ops anon_pidfd_ops = {
255 .class = "pidfd",
256 .get_name = anon_pidfd_get_name,
257 .fill_column = anon_pidfd_fill_column,
258 .init = anon_pidfd_init,
259 .free = anon_pidfd_free,
260 .handle_fdinfo = anon_pidfd_handle_fdinfo,
261 };
262
263 /*
264 * generic (fallback implementation)
265 */
266 static const struct anon_ops anon_generic_ops = {
267 .class = NULL,
268 .get_name = NULL,
269 .fill_column = NULL,
270 .init = NULL,
271 .free = NULL,
272 .handle_fdinfo = NULL,
273 };
274
275 const struct file_class unkn_class = {
276 .super = &file_class,
277 .size = sizeof(struct unkn),
278 .fill_column = unkn_fill_column,
279 .initialize_content = unkn_init_content,
280 .free_content = unkn_content_free,
281 .handle_fdinfo = unkn_handle_fdinfo,
282 };