(root)/
util-linux-2.39/
misc-utils/
lsfd-unkn.c
       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  };