Major
Major

Reputation: 199

How to use a variable as a format string in printk?

I try to use the command printk.

All the examples I can find on the internet is by putting a string directly in the printk like this:

printk(KERN_INFO "Hello %s!", "World");

However, I tried to replace the "Hello %s!" using a buffer like this:

char buf[] = "Hello %s!";
printk(KERN_INFO buf, "WORLD");

It turns out that I get the error

error: expected ')' before 'buf'

What should we do to using a variable in printk and also use a log level KERN_INFO?

Upvotes: 5

Views: 6409

Answers (4)

niziak
niziak

Reputation: 121

Please check struct va_format. It lets you pass existing format string and variable arguments as variable into new format string. Example usage:

va_list args;
struct va_format vaf = {
    .fmt = format,
};
va_start(args, format);
vaf.va = &args;
printk(KERN_DEBUG "[MY PREFIX] %pV", &vaf);
va_end(args);

Upvotes: 0

oscar1919
oscar1919

Reputation: 139

A possible workaround with three components: a macro which can generate a debug function with a specific loglevel, creation of this function with the loglevel wanted, another macro calling the created function with a variable number of arguments.
Calling LOGPRINTK(buf, "WORLD") does the job.

#include "stdarg.h"

// a macro which can generate a loglevelfunction based on the loglevel
// in kzalloc, do not forget the + 1 for the trailing zero
#define GENERATE_LOGLEVEL_FUNCTION(loglevelstring) void loglevelfunc(char *fmt, ...) { \
   char *modifiedfmt = NULL; \
   va_list input; \
   modifiedfmt = kzalloc((strlen(loglevelstring) + strlen(fmt) + 1 )*sizeof(char), GFP_KERNEL) ; \
   if (modifiedfmt) { \
      strcpy(modifiedfmt, loglevelstring); \
      va_start(input, fmt); \
      strcat(modifiedfmt, fmt); \
      vprintk(modifiedfmt, input) ; \
      va_end(input); \
      kfree(modifiedfmt); \
      modifiedfmt = NULL; \
    } \
}
// this line generates the log level function, with loglevel KERN_INFO
GENERATE_LOGLEVEL_FUNCTION(KERN_INFO)
// finally, the function we want
#define LOGPRINTK(...) loglevelfunc(__VA_ARGS__)

Upvotes: 0

Ctx
Ctx

Reputation: 18420

KERN_INFO is defined as string constants "\001" "6". When writing

printk(KERN_INFO "Hello %s!", "World");

the c compiler automatically concatenates the three string constants as required by the C standard:

"\001" "6" "Hello %s!"

to a single string constant. This, however, does not work with a variable, like buf is here:

char buf[] = "Hello %s!";
printk(KERN_INFO buf, "WORLD");

What will work is:

char buf[] = KERN_INFO "Hello %s!";
printk(buf, "WORLD");

Upvotes: 6

Govind Parmar
Govind Parmar

Reputation: 21542

KERN_INFO is a macro defined in the Linux kernel headers that expands to a string literal when the preprocessor runs. When placing string literals adjacently in C code they are implicitly concatenated; when placing a variable between string literals, it's a syntax error.

If you preprocess your code to a file, you will observe this more easily.

Upvotes: 2

Related Questions