De onde o uname obtém suas informações?

39

De onde o uname realmente obtém suas informações?

Eu acho que isso é algo que deve ser simples. Infelizmente, não consigo encontrar nenhum cabeçalho contendo apenas essas informações.

Digamos que alguém queira alterar a saída básica de uname / uname -s de Linux para outra coisa (essencialmente, renomeando o kernel).

Como ele faria isso da maneira correta (ou seja, mudar a fonte)?

    
por user237251 13.06.2014 / 15:56

8 respostas

26

O utilitário uname obtém suas informações da chamada do sistema uname() . Ele preenche uma estrutura como essa (consulte man 2 uname ):

       struct utsname {
           char sysname[];    /* Operating system name (e.g., "Linux") */
           char nodename[];   /* Name within "some implementation-defined
                                 network" */
           char release[];    /* Operating system release (e.g., "2.6.28") */
           char version[];    /* Operating system version */
           char machine[];    /* Hardware identifier */
       #ifdef _GNU_SOURCE
           char domainname[]; /* NIS or YP domain name */
       #endif
       };

Isso vem diretamente do kernel em execução. Eu diria que todas as informações são codificadas, exceto talvez domainname (e, como se vê, também nodename , machine e release , veja comentários). A string de lançamento, de uname -r , pode ser definida através da configuração em tempo de compilação, mas duvido muito que o campo sysname possa - é o kernel do Linux e não há razão concebível para usar qualquer outra coisa.

No entanto, como é open source, você pode alterar o código fonte e recompilar o kernel para usar qualquer nome que você quiser.

    
por 13.06.2014 / 16:10
24

Os dados são armazenados em init / version.c:

struct uts_namespace init_uts_ns = {
        .kref = {
                .refcount       = ATOMIC_INIT(2),
        },
        .name = {
                .sysname        = UTS_SYSNAME,
                .nodename       = UTS_NODENAME,
                .release        = UTS_RELEASE,
                .version        = UTS_VERSION,
                .machine        = UTS_MACHINE,
                .domainname     = UTS_DOMAINNAME,
        },
        .user_ns = &init_user_ns,
        .proc_inum = PROC_UTS_INIT_INO,
};
EXPORT_SYMBOL_GPL(init_uts_ns);

As próprias strings estão em include / generated / compile.h:

#define UTS_MACHINE "x86_64"
#define UTS_VERSION "#30 SMP Fri Apr 11 00:24:23 BST 2014"

e em include / generated / utsrelease.h:

#define UTS_RELEASE "3.14.0-v2-v"

UTS_SYSNAME pode ser definido em include / linux / uts.h

#ifndef UTS_SYSNAME
#define UTS_SYSNAME "Linux"
#endif

ou como #define em makefiles

Finalmente, o nome do host e o nome do domínio podem ser controlados por / proc / sys / kernel / {hostname, domainname}. Estes são por namespace UTS:

# hostname
hell
# unshare --uts /bin/bash
# echo test > /proc/sys/kernel/hostname 
# hostname
test
# exit
# hostname
hell
    
por 14.06.2014 / 02:03
8

Com a ajuda de uma referência cruzada do Linux e sua menção de /proc/sys/kernel/ostype , acompanhei ostype para include / linux / sysctl.h , onde um comentário diz que os nomes são adicionados chamando register_sysctl_table .

Então onde é que é chamado ? Um lugar é kernel / utsname_sysctl.c , que inclui include/linux/uts.h , onde encontramos:

/*
 * Defines for what uname() should return 
 */
#ifndef UTS_SYSNAME
#define UTS_SYSNAME "Linux"
#endif

Assim, como a documentação do kernel :

The only way to tune these values is to rebuild the kernel

: -)

    
por 14.06.2014 / 00:47
5

Como comentado em outro lugar, as informações vêm com o uname syscall, cuja informação é codificada no kernel em execução.

A parte da versão é normalmente definida ao compilar um novo kernel pelo Makefile :

VERSION = 3
PATCHLEVEL = 15
SUBLEVEL = 0
EXTRAVERSION =

quando eu tive tempo de jogar meus kernels, eu costumava adicionar coisas lá no EXTRAVERSION; que lhe deu uname -r com coisas como 3.4.1-mytestkernel .

Eu não entendo completamente, mas acho que o resto das informações está configurado no Makefile também na linha 944:

# ---------------------------------------------------------------------------

# KERNELRELEASE can change from a few different places, meaning version.h
# needs to be updated, so this check is forced on all builds

uts_len := 64
define filechk_utsrelease.h
    if [ 'echo -n "$(KERNELRELEASE)" | wc -c ' -gt $(uts_len) ]; then \
      echo '"$(KERNELRELEASE)" exceeds $(uts_len) characters' >&2;    \
      exit 1;                                                         \
    fi;                                                               \
    (echo \#define UTS_RELEASE \"$(KERNELRELEASE)\";)
endef

define filechk_version.h
    (echo \#define LINUX_VERSION_CODE $(shell                         \
    expr $(VERSION) \* 65536 + 0$(PATCHLEVEL) \* 256 + 0$(SUBLEVEL)); \
    echo '#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))';)
endef

$(version_h): $(srctree)/Makefile FORCE
    $(call filechk,version.h)

include/generated/utsrelease.h: include/config/kernel.release FORCE
    $(call filechk,utsrelease.h)

PHONY += headerdep
headerdep:
    $(Q)find $(srctree)/include/ -name '*.h' | xargs --max-args 1 \
    $(srctree)/scripts/headerdep.pl -I$(srctree)/include

Para o restante dos dados, o sys_uname syscall é gerado usando macros (de uma maneira bastante complicada), você pode iniciar em aqui se você se sentir aventureiro.

Provavelmente, a melhor maneira de alterar essas informações é escrever um módulo do kernel para substituir o uname syscall; Eu nunca fiz isso, mas você pode encontrar informações nesta página na seção 4.2 (desculpe, sem link direto). Observe, no entanto, que esse código está se referindo a um kernel bastante antigo (agora o kernel do Linux possui uts namespaces, o que quer que isso signifique), portanto, você precisará alterá-lo provavelmente muito.

    
por 13.06.2014 / 19:19
2

Embora não consegui encontrar nada na fonte para indicar isso, acredito que use o syscall uname.

man 2 uname

deve dizer mais sobre isso. Se for esse o caso, obter as informações diretamente do kernel e alterá-las provavelmente exigiria recompilação.

Você pode mudar o binário para o seu nome para fazer o que quiser, mas escreva sobre ele com o programa que quiser. A desvantagem de alguns scripts dependem dessa saída.

    
por 13.06.2014 / 16:08
1

A maneira correta de mudar o uname seria alterar os cabeçalhos de compilação e recompilar como os outros sugeriram. Mas eu não tenho certeza porque você gostaria de passar por tantos problemas quando você pode fazer algo como,

alias uname 'uname \!* | sed s/2.6.13/2.6.52/'

ou até mesmo

alias uname 'echo whatever'
    
por 26.01.2017 / 21:16
0

A resposta do Rmano me pegou no meio do caminho, mas a mágica real é mais fácil de ser descoberta passando o Q= opção na sua linha de comando make no diretório de origem do kernel. Ele permite que você veja os detalhes, um dos quais é uma chamada para um script: echo "4.4.19$(/bin/sh ./scripts/setlocalversion .)" . A execução desse mesmo snippet fornece o número de release do kernel, 4.4.19-00010-ge5dddbf . Se você observar o script, ele determinará o número do sistema de versão e a execução com bash -x mostrará o processo exato:

+++ git rev-parse --verify --short HEAD
++ head=e5dddbf
+++ git describe --exact-match
++ '[' -z '' ']'
++ false
+++ git describe
++ atag=release/A530_os_1.0.0-10-ge5dddbf
++ echo release/A530_os_1.0.0-10-ge5dddbf
++ awk -F- '{printf("-%05d-%s", $(NF-1),$(NF))}'
++ git config --get svn-remote.svn.url
++ git diff-index --name-only HEAD
++ grep -qv '^scripts/package'
++ return
+ res=-00010-ge5dddbf
+ echo -00010-ge5dddbf
-00010-ge5dddbf

o que isso me mostra é que, se eu quiser construir um módulo do kernel para trabalhar com o meu kernel em execução, estou na versão incorreta marcada e no commit errado. Eu preciso consertar isso e construir pelo menos os DTBs ( make dtbs ) para criar os arquivos gerados com o número correto da versão.

Acontece que nem isso foi suficiente. Eu tive que substituir scripts/setlocalversion por um que simplesmente faz:

#!/bin/sh
echo -0710GC0F-44F-01QA

, em seguida, reconstrua os arquivos gerados automaticamente:

make Q= ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- dtbs

então eu poderia criar o driver de amostra de Derek Molloy e foi capaz de insmod com sucesso. aparentemente o aviso sobre Module.symvers não estar presente não importava. tudo o que o Linux estava usando para determinar se o módulo funcionaria era aquela string de versão local.

    
por 16.07.2017 / 02:06
0

scripts/mkcompile_h

Na v4.19, esse é o arquivo que gera include/generated/compile.h e contém várias partes interessantes de /proc/version : link

  • a parte #<version> vem do arquivo .version na árvore de construção, que é incrementada sempre que o link acontece (requer alterações de arquivo / configuração) por scripts/link-vmlinux.sh .

    Ele pode ser substituído pela variável de ambiente KBUILD_BUILD_VERSION :

    if [ -z "$KBUILD_BUILD_VERSION" ]; then
        VERSION=$(cat .version 2>/dev/null || echo 1)
    else
        VERSION=$KBUILD_BUILD_VERSION
    fi
    
  • a data é apenas uma chamada date bruta:

    if [ -z "$KBUILD_BUILD_TIMESTAMP" ]; then
        TIMESTAMP='date'
    else
        TIMESTAMP=$KBUILD_BUILD_TIMESTAMP
    fi
    

    e da mesma forma o nome de usuário vem de whoami ( KBUILD_BUILD_USER ) e hostname de hostname ( KBUILD_BUILD_HOST )

  • A versão do compilador vem de gcc -v e não pode ser controlada, parece.

Aqui está uma forma de alterar a versão da questão:

    
por 04.12.2018 / 19:20