53

I am trying to write some custom messages in my dmesg output. I tried:

logger "Hello"

but this does not work. It exits without error, but no "Hello" appears int the output of:

dmesg

I am using a Fedora 9, and it seems that there is no syslogd/klogd daemon running. However, all my kernel messages are succesfully written in the dmesg buffer.

Any idea?

7 Answers 7

44

dmesg displays what is in the kernel buffer, whereas logger is for syslogd. I think if you want to print things into the kernel buffer you will need to create a driver that uses the printk() kernel function. If you just want it in /var/log/messages, then with a "normal" setup I think what you have done with logger is already fine.

The most basic example of a driver with printk() would be:

hello.c:

#include <linux/module.h>
#include <linux/kernel.h>

int init_module(void)
{
    printk(KERN_INFO "Hello world\n");
    return 0;
}

void cleanup_module(void)
{
    printk(KERN_INFO "Goodbye world\n");

}

Makefile:

obj-m += hello.o

all:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

Then:

$ make
$ sudo insmod hello.ko
$ dmesg | tail -n1
 [7089996.746366] Hello world

http://tldp.org/LDP/lkmpg/2.6/html/lkmpg.html#AEN121 for more...

3
  • I got an error, since you have put spaces before the make -C ... in the Makefile instead of a Tab, so copying the above contents of the Makefile does not work - more here. I appear to be unable to add this in an edit... Thanks by the way, great answer.
    – Wilf
    Jul 11, 2014 at 18:17
  • syslog messages are now in /var/log/syslog and not anymore in /var/log/messages. (Changed for Ubuntu at least. Other distros might use the old location.)
    – tanius
    Oct 11, 2020 at 22:32
  • Improved version of the driver he wrote can be found here: serverfault.com/questions/140354/…
    – linuxgeek
    Aug 22, 2023 at 19:10
137

You can, as root, write to /dev/kmsg to print to the kernel message buffer:

 fixnum:~# echo Some message > /dev/kmsg
 fixnum:~# dmesg | tail -n1
 [28078118.692242] Some message

I've tested this on my server and an embedded Linux device, and it works on both, so I'm just going to assume it works pretty much everywhere.

5
  • 1
    Interesting that in Ubuntu, this works as root but not with sudo. One actually needs to become root.
    – dotancohen
    Jun 30, 2012 at 8:53
  • 22
    Actually, that's because the input redirection is handled by your shell, which is not running with elevated rights. Try running echo Some message | sudo tee /dev/kmesg as non-root.
    – wvdschel
    Jul 4, 2012 at 11:56
  • 6
    That works. Thanks, interesting. By the way, its kmsg not kmesg but I also confuse with dmesg which has the e!
    – dotancohen
    Jul 4, 2012 at 14:55
  • 5
    Much easier than compiling kernel module
    – e271p314
    Mar 3, 2014 at 9:22
  • 3
    For correct formatting in the syslog, be sure to add a priority (delimited with angles) and a tag (delimited by a colon). echo '<4>Foo: Message' | sudo tee /dev/kmsg
    – Renate
    Jan 10, 2020 at 23:37
14

Based on Kyle's module above:


#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/proc_fs.h>
#include <asm/uaccess.h>

static int pk_write(struct file *file, const char *buffer, unsigned long count, void *data)
{
        char string[256];
        count = count < 255 ? count : 255;

        if(copy_from_user(string, buffer, count))
                return -EFAULT;

        string[count] = '\0';        
        printk(string);
        return count;
}


static int __init printk_init(void)
{
        struct proc_dir_entry *pk_file;

        pk_file = create_proc_entry("printk", 0222, NULL);
        if(pk_file == NULL)
                return -ENOMEM;

        pk_file->write_proc = pk_write;
        pk_file->owner = THIS_MODULE;

        return 0;
}

static void __exit printk_cleanup(void)
{
        remove_proc_entry("printk", NULL);
}

module_init(printk_init);
module_exit(printk_cleanup);
MODULE_LICENSE("GPL");

To do a printk from user space:

echo "Hello" > /proc/printk
1
  • 1
    This works for Linux kernel < 3.10 only. See my answer for a newer alternative.
    – Kevin
    Nov 25, 2015 at 21:00
6

@Calandoa's answer no longer works for Kernel +3.10. Combined his code, and the example code I found here. Then improved on the code quality...

Code saved to printk_user.c

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/proc_fs.h>
#include <asm/uaccess.h>

static ssize_t write_proc(struct file *filep, const char *buffer, size_t count, loff_t *offsetp)
{
    char string[256];
    count = count < 255 ? count : 255;

    if(copy_from_user(string, buffer, count) != 0) {
        return -EFAULT;
    }

    string[count] = '\0';
    printk(string);
    return count;
}

static const struct file_operations proc_fops = {
    .owner = THIS_MODULE,
    .write = write_proc,
};

static int proc_init(void) {
    struct proc_dir_entry *proc_file;
    proc_file = proc_create("printk_user", 0, NULL, &proc_fops);

    if(proc_file == NULL) {
        return -ENOMEM;
    }

    return 0;
}

static void proc_cleanup(void) {
    remove_proc_entry("printk_user", NULL);
}

MODULE_LICENSE("GPL"); 
module_init(proc_init);
module_exit(proc_cleanup);

Make using this Makefile

TARGET = printk_user
obj-m := $(TARGET).o
    
KERNEL_VERSION=$(shell uname -r)
KDIR = /lib/modules/$(KERNEL_VERSION)/build
PWD = $(shell pwd)
    
printk:
$(MAKE) -C $(KDIR) M=$(PWD) modules
    
clean:
$(MAKE) -C $(KDIR) M=$(PWD) clean

You'll need your current running kernel source code installed in order to make this. After you run make you will need to sudo insmod printk_user.ko to load the kernel module. The module creates /proc/printk_user, by default with permissions 444. If you need all users to access, modify with sudo chmod 666 /proc/printk_user

To do a printk from user space:

echo "Hello" > /proc/printk_user

Sample output:

$ dmesg | tail
[13291030.939275] printk_user: loading out-of-tree module taints kernel.
[13291030.939862] printk_user: module verification failed: signature and/or required key missing - tainting kernel
[13291103.728306] Hello

edit: /proc/printk used to be for Kernel 3/4, but it appears the latest kernel has support for /proc/printk_user

3

Based off of Kyle's answer, here is a quick tutorial showing how to do just that.

3

Figured I'd go ahead and include a full blown example of something that people can just compile and run for those that aren't as skilled with C based off of @BuvinJ 's answer

#include <stdio.h>
#include <string.h>
#include <fcntl.h> // open function
#include <unistd.h> // close function
#include "sys/syscall.h"


int main(); // Let's not worry about this for now

void dmesg( const char *tag, const char *msg, const int len )
{
    const int TAG_LEN=3;
    char buffer[128]={0};
    memcpy( &buffer[0], tag, TAG_LEN );
    memcpy( &buffer[TAG_LEN], msg, len );
    int fd_kmsg = open( "/dev/kmsg", O_WRONLY );
    write( fd_kmsg, &buffer, TAG_LEN+len );
    close( fd_kmsg );
}
void dmesgWarn(  const char *msg, const int len ){ dmesg( "<4>", msg, len ); }
void dmesgInfo(  const char *msg, const int len ){ dmesg( "<6>", msg, len ); }
void dmesgDebug( const char *msg, const int len ){ dmesg( "<7>", msg, len ); }


int main(int argc, char **argv)
{
    int getmysize = strlen(argv[1]);
    printf("%d\n", getmysize);
    
    printf("To be written: %s\nSize of argument: %d\n", argv[1], getmysize);
    // dmesgWarn dmesgInfo or dmesgDebug
    dmesgDebug(argv[1], getmysize);
};

To run save the above as kmsg.c then execute gcc kmsg.c -o kmsg and run as sudo ./kmsg "string you want to add to /dev/kmsg"

2

I just wanted some quick debugging messages in a daemon written by someone else in a cross complied kernel. I ran into a compile error trying to use printk, as <linux/module.h> could not be included. Rather then battle with that excessively (to do this the right way) I cheated and used the following lazy, but functional 5 minute workaround:

void dmesg( const char *tag, const char *msg, const int len )
{
    size_t taglen = strlen(tag);
    char buffer[taglen + len];
    memcpy(&buffer[0], tag, taglen);
    memcpy(&buffer[taglen], msg, len);
    int fd_kmsg = open("/dev/kmsg", O_WRONLY);
    write(fd_kmsg, &buffer, TAG_LEN + len);
    close(fd_kmsg);
}
void dmesgWarn(const char *msg, const int len) { dmesg("<4>", msg, len); }
void dmesgInfo(const char *msg, const int len) { dmesg("<6>", msg, len); }
void dmesgDebug(const char *msg, const int len) { dmesg("<7>", msg, len); }

UPDATE (Thanks @glglgl!)

A much simpler version could be like this:

void dmesg( const unsigned int tag, const char *msg)
{
    size_t msglen = sprintf(NULL, "<%u>%s", tag, msg);
    char buffer[msglen + 1];
    sprintf(buffer, "<%u>%s", tag, msg);
    // snprintf(buffer, sizeof(buffer), "<%u>%s", tag, msg);
    // would be safer, but here we make sure that everything works as it should.
    int fd_kmsg = open("/dev/kmsg", O_WRONLY);
    write(fd_kmsg, &buffer, msglen);
    close(fd_kmsg);
}
void dmesgWarn(const char *msg) { dmesg(4, msg); }
void dmesgInfo(const char *msg) { dmesg(6, msg); }
void dmesgDebug(const char *msg) { dmesg(7, msg); }

It now just takes strings, integrates them in a message to be written to that file and writes it.

Now that we talk about it, it can be even much easier:

void dmesg( const unsigned int tag, const char *msg)
{
    int fd_kmsg = open("/dev/kmsg", O_WRONLY);
    FILE * f_kmsg = fdopen(fd_kmsg, "w");
    fprintf(f_kmsg, "<%u>%s", tag, msg);
    fclose(f_kmsg); // closes the underlying fd_kmsg as well
}
void dmesgWarn(const char *msg) { dmesg(4, msg); }
void dmesgInfo(const char *msg) { dmesg(6, msg); }
void dmesgDebug(const char *msg) { dmesg(7, msg); }
6
  • size_t taglen = strlen(tag); char buffer[taglen + len]; memcpy( &buffer[0], tag, taglen); memcpy( &buffer[taglen], msg, len ); would do the trick as well.
    – glglgl
    Jul 26, 2022 at 7:42
  • Ok. I infer you are eliminating the need for the len argument? You could directly suggest an edit in that case, if you were so inclined.
    – BuvinJ
    Jul 26, 2022 at 13:30
  • Not for the len argument (that could be done as well), but for the constant TAG_LEN and the constant buffer[] size.
    – glglgl
    Jul 27, 2022 at 7:14
  • 1
    @glglgl Please suggest an "edit" directly. That is, you can modify my answer to improved it. Note that I wrote this was a "lazy, but functional 5 minute workaround". If you want to tweak this so it becomes a better answer, I certainly have no objection!
    – BuvinJ
    Jul 27, 2022 at 12:42
  • Ok, I just did so.
    – glglgl
    Jul 27, 2022 at 13:15

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .