Re: [PATCH] util-linux/sys-utils dmesg add PRINTK_CALLER support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 2023-12-11 09:23:37-0800, Edward Chron wrote:
> Submission to Project: util-linux
> Open Incident: #2906 at github.com/util-linux/util-linux/issues/2906
> Component: util-linux/sys-utils
> File: dmesg.c
> Code level patch applied against: 2.39.3 - latest code pulled from
>            git.github.com:util-linux/util-linux.git
> Revision: #1 on 2023/12/08 per Review from Karel Zak

[..]

> ---
>  include/pathnames.h |   3 ++
>  sys-utils/dmesg.c   | 100 +++++++++++++++++++++++++++++++++++++++++++-
>  2 files changed, 102 insertions(+), 1 deletion(-)
> 
> diff --git a/include/pathnames.h b/include/pathnames.h
> index caf0e63d4..81fa405f6 100644
> --- a/include/pathnames.h
> +++ b/include/pathnames.h
> @@ -230,4 +230,7 @@
>  /* cgroup path */
>  #define _PATH_SYS_CGROUP	"/sys/fs/cgroup"
>  
> +/* Maximum number of PIDs system supports */
> +#define _PATH_PROC_PIDMAX	"/proc/sys/kernel/pid_max"
> +
>  #endif /* PATHNAMES_H */
> diff --git a/sys-utils/dmesg.c b/sys-utils/dmesg.c
> index 79e1c1690..fc7252539 100644
> --- a/sys-utils/dmesg.c
> +++ b/sys-utils/dmesg.c
> @@ -8,6 +8,7 @@
>   */
>  #include <stdio.h>
>  #include <getopt.h>
> +#include <stdbool.h>
>  #include <stdlib.h>
>  #include <sys/klog.h>
>  #include <sys/syslog.h>
> @@ -36,6 +37,7 @@
>  #include "mangle.h"
>  #include "pager.h"
>  #include "jsonwrt.h"
> +#include "pathnames.h"
>  
>  /* Close the log.  Currently a NOP. */
>  #define SYSLOG_ACTION_CLOSE          0
> @@ -60,6 +62,11 @@
>  /* Return size of the log buffer */
>  #define SYSLOG_ACTION_SIZE_BUFFER   10
>  
> +#define PID_CHARS_MAX sizeof(stringify_value(LONG_MAX))
> +#define PID_CHARS_DEFAULT sizeof(stringify_value(INT_MAX))
> +#define DMESG_CALLER_PREFIX "caller="
> +#define DMESG_CALLER_PREFIXSZ (sizeof(DMESG_CALLER_PREFIX)-1)
> +
>  /*
>   * Color scheme
>   */
> @@ -216,6 +223,7 @@ struct dmesg_control {
>  			force_prefix:1;	/* force timestamp and decode prefix
>  					   on each line */
>  	int		indent;		/* due to timestamps if newline */
> +	size_t          caller_id_size;   /* PRINTK_CALLERID max field size */
>  };
>  
>  struct dmesg_record {
> @@ -225,6 +233,7 @@ struct dmesg_record {
>  	int		level;
>  	int		facility;
>  	struct timeval  tv;
> +	char		caller_id[PID_CHARS_MAX];
>  
>  	const char	*next;		/* buffer with next unparsed record */
>  	size_t		next_size;	/* size of the next buffer */
> @@ -237,6 +246,7 @@ struct dmesg_record {
>  		(_r)->level = -1; \
>  		(_r)->tv.tv_sec = 0; \
>  		(_r)->tv.tv_usec = 0; \
> +		(_r)->caller_id[0] = 0; \
>  	} while (0)
>  
>  static int process_kmsg(struct dmesg_control *ctl);
> @@ -532,6 +542,45 @@ static int get_syslog_buffer_size(void)
>  	return n > 0 ? n : 0;
>  }
>  
> +/*
> + * Get the number of characters needed to hold the maximum number
> + * of tasks this system supports. This size of string could hold
> + * a thread id large enough for the highest thread id.
> + * This is needed to determine the number of characters reserved for
> + * the PRINTK_CALLER field if it has been configured in the Linux Kernel.
> + *
> + * The number of digits sets the max value since the value can't exceed
> + * a value of that size. The /proc field defined by _PATH_PROC_PIDMAX
> + * holds the maximum number of PID values that may be ussed by the system,
> + * so 0 to that value minus one.
> + *
> + * For determining the size of the PRINTK_CALLER field, we make the safe
> + * assumption that the number of threads >= number of cpus. This because
> + * the PRINTK_CALLER field can hold either a thread id or a CPU id value.
> + *
> + * If we can't access the pid max kernel proc entry we assign a default
> + * field size of 5 characters as that is what the old syslog interface
> + * uses as the reserved field size. This is justified because 32-bit Linux
> + * systems are limited to PID values between (0-32767).
> + *
> + */
> +static size_t max_threads_id_size(void)
> +{
> +	char taskmax[PID_CHARS_MAX] = {'\0'};
> +	ssize_t rdsize;
> +	int fd;
> +
> +	fd = open(_PATH_PROC_PIDMAX, O_RDONLY);
> +	if (fd == -1)
> +		return PID_CHARS_DEFAULT;
> +
> +	rdsize = read(fd, taskmax, sizeof(taskmax));
> +	if (rdsize == -1)
> +		return PID_CHARS_DEFAULT;
> +
> +	return strnlen(taskmax, sizeof(taskmax));
> +}
> +
>  /*
>   * Reads messages from regular file by mmap
>   */
> @@ -706,6 +755,36 @@ static const char *skip_item(const char *begin, const char *end, const char *sep
>  	return begin;
>  }
>  
> +/*
> + * Checks to see if the caller (caller id) field is present in the kmsg record.
> + * This is true if the PRINTK_CALLER config option has been set in the kernel.
> + *
> + * If the caller_id is found in the kmsg buffer then return the id and id type
> + * to the caller in dmesg caller_id. Returns string pointer to next value.
> + *
> + */
> +static const char *parse_callerid(const char *p_str, const char *end,
> +				  struct dmesg_record *p_drec)
> +{
> +	const char *p_after;
> +	const char *p_next;
> +	size_t cid_size;
> +	char *p_cid;
> +
> +	p_cid = strstr(p_str, DMESG_CALLER_PREFIX);

Could this be made a bit more robust to only look for the field in the
expected location and not all of the message?

> +	if (p_cid != NULL && p_drec != NULL) {
> +		p_next = p_cid + DMESG_CALLER_PREFIXSZ;
> +		p_after = skip_item(p_next, end, ",;");
> +		cid_size = p_after - p_next;
> +		if (cid_size < sizeof(p_drec->caller_id))
> +			xstrncpy(p_drec->caller_id, p_next, cid_size);
> +		else
> +			return p_str;
> +		return p_after;
> +	}
> +	return p_str;
> +}
> +
>  /*
>   * Parses one record from syslog(2) buffer
>   */
> @@ -1079,6 +1158,19 @@ full_output:
>  			color_disable();
>  	}
>  
> +	if (*rec->caller_id) {
> +		if (ctl->json) {
> +			ul_jsonwrt_value_s(&ctl->jfmt, "caller", rec->caller_id);
> +		} else {
> +			char cidbuf[PID_CHARS_MAX+3] = {'\0'};
> +
> +			sprintf(cidbuf, "[%*s] ",
> +				(char)ctl->caller_id_size - 1, rec->caller_id);
> +			ctl->indent += strnlen(cidbuf, PID_CHARS_MAX+3);

Use sizeof(cidbuf);

> +			fputs(cidbuf, stdout);
> +		}
> +	}
> +
>  	/*
>  	 * A kernel message may contain several lines of output, separated
>  	 * by '\n'.  If the timestamp and decode outputs are forced then each
> @@ -1207,6 +1299,8 @@ static int init_kmsg(struct dmesg_control *ctl)
>  		return -1;
>  	}
>  
> +	ctl->caller_id_size = max_threads_id_size();
> +
>  	return 0;
>  }
>  
> @@ -1262,7 +1356,10 @@ static int parse_kmsg_record(struct dmesg_control *ctl,
>  		goto mesg;
>  
>  	/* D) optional fields (ignore) */
> -	p = skip_item(p, end, ";");
> +	p = skip_item(p, end, ",;");
> +
> +	/* Include optional PRINTK_CALLER field if it is present */
> +	p = parse_callerid(p, end, rec);
>  
>  mesg:
>  	/* E) message text */
> @@ -1390,6 +1487,7 @@ int main(int argc, char *argv[])
>  		.kmsg = -1,
>  		.time_fmt = DMESG_TIMEFTM_TIME,
>  		.indent = 0,
> +		.caller_id_size = 0,
>  	};
>  	int colormode = UL_COLORMODE_UNDEF;
>  	enum {
> -- 
> 2.43.0
> 
> 




[Index of Archives]     [Netdev]     [Ethernet Bridging]     [Linux Wireless]     [Kernel Newbies]     [Security]     [Linux for Hams]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux Admin]     [Samba]

  Powered by Linux