According to the Linux online manual ("man 5
core"), the /proc/sys/kernel/core_pattern file can be set to
define a template that is used to name core dump files.
The template can contain % specifiers which are substituted by
the following values when a core file is created:
- %% a single % character
- %c core file size soft resource limit of crashing process
- %d dump mode—same as value returned by prctl(2) PR_GET_DUMPABLE
- %e executable filename (without path prefix)
- %E pathname of executable, with slashes ('/') replaced by exclamation marks ('!')
- %g (numeric) real GID of dumped process
- %h hostname (same as nodename returned by uname(2))
- %i TID of thread that triggered core dump, as seen in the PID namespace in which the thread resides
- %I TID of thread that triggered core dump, as seen in the initial PID namespace
- %p PID of dumped process, as seen in the PID namespace in which the process resides
- %P PID of dumped process, as seen in the initial PID namespace
- %s number of signal causing dump
- %t time of dump, expressed as seconds since the Epoch, 1970-01-01 00:00:00 +0000 (UTC)
- %u (numeric) real UID of dumped process
A single "%" at the end of the template is
dropped from the core filename, as is the combination of a %
followed by any character other than those listed above.
Moreover, if the first character is a "|" directly followed by a program's pathname, the latter is run in the initial namespaces (PID, mount, user and so on) and with the content of the core passed into its standard input. The preceding list of "%" specifiers can be used on the command line arguments of the program. For example, on my Ubuntu system, The file contains:
|/usr/share/apport/apport %p %s %c %d %P
The manual specifies that the process runs in the initial namespaces (PID, mount, user, and so on) and not in the namespaces of the crashing process. One can utilize specifiers such as %P to find the right /proc/[pid] directory and probe/enter the crashing process's namespaces if needed.
But what about "%h" ? The manual merely says
that it is the hostname. But which one ? The crashing process
one or the initial one ?
In the Linux source code, the "%" parameters are substituted in "fs/coredump.c":
/* format_corename will inspect the pattern parameter, and output a * name into corename, which must have space for at least * CORENAME_MAX_SIZE bytes plus one byte for the zero terminator. */ static int format_corename(struct core_name *cn, struct coredump_params *cprm, size_t **argv, int *argc) { const struct cred *cred = current_cred(); const char *pat_ptr = core_pattern; int ispipe = (*pat_ptr == '|'); bool was_space = false; int pid_in_pattern = 0; int err = 0; cn->used = 0; cn->corename = NULL; if (expand_corename(cn, core_name_size)) return -ENOMEM; cn->corename[0] = '\0'; if (ispipe) { int argvs = sizeof(core_pattern) / 2; (*argv) = kmalloc_array(argvs, sizeof(**argv), GFP_KERNEL); if (!(*argv)) return -ENOMEM; (*argv)[(*argc)++] = 0; ++pat_ptr; } /* Repeat as long as we have more pattern to process and more output space */ while (*pat_ptr) { /* * Split on spaces before doing template expansion so that * %e and %E don't get split if they have spaces in them */ if (ispipe) { if (isspace(*pat_ptr)) { was_space = true; pat_ptr++; continue; } else if (was_space) { was_space = false; err = cn_printf(cn, "%c", '\0'); if (err) return err; (*argv)[(*argc)++] = cn->used; } } if (*pat_ptr != '%') { err = cn_printf(cn, "%c", *pat_ptr++); } else { switch (*++pat_ptr) { /* single % at the end, drop that */ case 0: goto out; /* Double percent, output one percent */ case '%': err = cn_printf(cn, "%c", '%'); break; /* pid */ case 'p': pid_in_pattern = 1; err = cn_printf(cn, "%d", task_tgid_vnr(current)); break; /* global pid */ case 'P': err = cn_printf(cn, "%d", task_tgid_nr(current)); break; case 'i': err = cn_printf(cn, "%d", task_pid_vnr(current)); break; case 'I': err = cn_printf(cn, "%d", task_pid_nr(current)); break; /* uid */ case 'u': err = cn_printf(cn, "%u", from_kuid(&init_user_ns, cred->uid)); break; /* gid */ case 'g': err = cn_printf(cn, "%u", from_kgid(&init_user_ns, cred->gid)); break; case 'd': err = cn_printf(cn, "%d", __get_dumpable(cprm->mm_flags)); break; /* signal that caused the coredump */ case 's': err = cn_printf(cn, "%d", cprm->siginfo->si_signo); break; /* UNIX time of coredump */ case 't': { time64_t time; time = ktime_get_real_seconds(); err = cn_printf(cn, "%lld", time); break; }/* hostname */ case 'h': down_read(&uts_sem); err = cn_esc_printf(cn, "%s", utsname()->nodename); up_read(&uts_sem); break;/* executable */ case 'e': err = cn_esc_printf(cn, "%s", current->comm); break; case 'E': err = cn_print_exe_file(cn); break; /* core limit size */ case 'c': err = cn_printf(cn, "%lu", rlimit(RLIMIT_CORE)); break; default: break; } ++pat_ptr; } if (err) return err; } out: /* Backward compatibility with core_uses_pid: * * If core_pattern does not include a %p (as is the default) * and core_uses_pid is set, then .%pid will be appended to * the filename. Do not do this for piped commands. */ if (!ispipe && !pid_in_pattern && core_uses_pid) { err = cn_printf(cn, ".%d", task_tgid_vnr(current)); if (err) return err; } return ispipe; }
We can see in the previous code that "%h" is substituted by "utsname()->nodename". The "utsname()" function is defined in "include/linux/utsname.h" as follow:
static inline struct new_utsname *utsname(void) { return ¤t->nsproxy->uts_ns->name; }
In other words, the host name is the one defined in the uts_namespace of the crashing task (it is not the host name of the initial namespace).
The author is an engineer in computer sciences located in France. He can be contacted here.