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

40

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

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

Digamos que alguém quisesse alterar a saída básica de uname/ uname -s de Linuxpara outra (essencialmente, renomear o kernel).

Como ele / ela faria isso da maneira correta (isto é, mudar a fonte)?

user237251
fonte

Respostas:

26

O unameutilitário obtém suas informações da uname()chamada do sistema. Ele preenche uma estrutura como esta (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 toda a informação é codificado para ele, exceto, talvez domainname(e como se vê, também nodename, machinee release, veja comentários). A string de lançamento, from uname -r, pode ser definida via 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 é de código aberto, você pode alterar o código-fonte e recompilar o kernel para usar o nome de sistema que desejar.

Cachinhos Dourados
fonte
2
O domainnamecampo é definido pelo domainnamecomando, usando a setdomainnamechamada do sistema. Da mesma forma, o nodenamecampo é definido pelo hostnamecomando, usando a sethostnamechamada do sistema. (A nodename/ hostnamevalor pode ser armazenado no /etc/nodename.)
Scott
2
Isso é irrelevante - a pergunta foi feita onde mudar isso. Então, sim, o unamecomando obtém suas informações de uma chamada do sistema. E de onde a chamada do sistema obtém suas informações? (Resposta, fornecida por outros pôsteres aqui: ela é codificada no kernel em tempo de compilação.)
Gilles 'SO- stop be evil' -
@Gilles: O que é irrelevante? Se a resposta for "fornecida por outros pôsteres aqui: está codificada no kernel ..." note que eu disse exatamente a mesma coisa: "Isso vem diretamente do kernel em execução. Suponho que todas as informações sejam difíceis -coded para ele ... uma vez que é open source, você pode alterar o código fonte e recompilar o kernel para usar o que sysname você quer é. não uma opção de configuração.
Goldilocks
2
@goldilocks Por que machinealguma vez mudaria? Pode não ser codificado no kernel porque pode se adaptar ao hardware, mas certamente será definido no momento da inicialização e não será alterado depois disso. Mas não: pode ser definido por processo (por exemplo, para reportar i686para 32 bits processados ​​em x86_64). A propósito, releasetambém pode ser personalizado por processo até certo ponto (tentativa setarch i686 --uname-2.6 uname -a).
Gilles 'SO- stop be evil'
11
@ Gilles eu editei machine, nodenamee releasena pergunta com uma referência aos comentários. Novamente, a questão não era realmente sobre todos esses campos.
goldilocks
26

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 seqüências de caracteres estão em include / generate / compile.h:

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

e em incluir / gerado / 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 espaço de nome UTS:

# hostname
hell
# unshare --uts /bin/bash
# echo test > /proc/sys/kernel/hostname 
# hostname
test
# exit
# hostname
hell
V13
fonte
Geralmente, essa é uma resposta boa e completa, mas pode valer a pena responder diretamente à pergunta do pôster. Eu acredito que isso equivaleria a - alterar a entrada relevante no arquivo relevante e recompilar. Você escreveu "ou como #define em makefiles". Você pode elaborar?
Faheem Mitha
+1 para unshare. De alguma forma eu consegui perder esse comando até hoje. Obrigado!
Tino
E include/generated/compile.hé gerado por scripts/mkcompile_h: unix.stackexchange.com/a/485962/32558
Ciro Santilli ()
8

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

Então, de onde é que isso é chamado ? Um lugar é o 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

Portanto, como a documentação do kernel afirma:

A única maneira de ajustar esses valores é reconstruir o kernel

:-)

deltab
fonte
6

Como comentado em outro lugar, as informações vêm com o unamesyscall, que é codificado 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 tinha tempo para jogar compilando meus kernels, costumava adicionar coisas lá em EXTRAVERSION; que te deu uname -r coisas do tipo 3.4.1-mytestkernel.

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

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

# 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_unamesyscall é gerado usando macros (de uma maneira bastante complicada). Você pode começar daqui se sentir aventureiro.

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

Rmano
fonte
Obrigado a todos. Eu já sabia que tinha algo a ver com uname. No entanto, o que não consigo entender é como e onde, dentro da fonte, a string "Linux" é definida. Tudo o que sei é onde posso encontrar essas informações durante o tempo de execução (elas estão contidas em / proc / sys / kernel / ostype). Descobrir como exatamente o próprio kernel sabe que seu nome seria uma das coisas mais interessantes, eu diria.
user237251
@ user237251 Quantas instâncias da palavra "Linux" ocorrem na fonte do kernel em contextos de cadeia de caracteres? Se não são tantas, basta examinar os resultados de uma pesquisa textual e ver aonde isso o leva.
21414 JAB
@JAB Demasiados. Felizmente, alguém no kernelnewbies.org me ajudou a resolver o "mistério". O Linux recebe seu nome de sistema em /include/Linux/uts.h. Veja aqui: lxr.free-electrons.com/source/include/linux/uts.h?v=3.10
user237251
2

Embora eu não tenha encontrado nada na fonte para indicar isso, acredito que ele use o syscall uname.

man 2 uname

deveria lhe contar mais sobre isso. Se for esse o caso, ele obtém as informações diretamente do kernel e as altera provavelmente exigiria recompilação.

Você pode alterar o binário para que você não faça o que quiser, basta escrever sobre ele com o programa que desejar. A desvantagem é que alguns scripts contam com essa saída.

Livinglifeback
fonte
3
Se o fizer strace uname, confirmará que a unamechamada do sistema é usada.
Graeme
1

A maneira correta de alterar a uname seria alterar os cabeçalhos de compilação e recompilar como sugerido por outros. Mas não sei por que você gostaria de passar por tantos problemas quando pode fazer algo como,

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

ou mesmo

alias uname 'echo whatever'
Saurabh
fonte
0

A resposta de Rmano me levou a meio caminho, mas a verdadeira mágica é mais fácil de descobrir, passando a Q=opção em sua makelinha de comando 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 .)". executar esse mesmo trecho fornece o número da versão do kernel 4.4.19-00010-ge5dddbf,. se você olhar para o script, ele determina o número do sistema de controle de versão e a execução bash -xmostra 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 quero construir um módulo do kernel para trabalhar com o meu kernel em execução, estou no release marcado errado e no commit errado. Preciso corrigir isso e criar pelo menos os DTBs ( make dtbs) para criar os arquivos gerados com o número de versão correto.


Acontece que mesmo isso não foi suficiente. Eu tive que substituir scripts/setlocalversionpor um que simplesmente:

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

em seguida, recrie os arquivos gerados automaticamente:

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

então eu pude construir o driver de amostra de Derek Molloy e consegui com insmodsucesso. aparentemente o aviso sobre Module.symversnão estar presente não importava. todo o Linux estava usando para determinar se o módulo funcionaria era a string de versão local.

jcomeau_ictx
fonte
0

scripts/mkcompile_h

Na v4.19, esse é o arquivo que gera include/generated/compile.he contém várias partes interessantes de /proc/version: https://github.com/torvalds/linux/blob/v4.19/scripts/mkcompile_h

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

    Pode ser substituído pela KBUILD_BUILD_VERSIONvariável de ambiente:

    if [ -z "$KBUILD_BUILD_VERSION" ]; then
        VERSION=$(cat .version 2>/dev/null || echo 1)
    else
        VERSION=$KBUILD_BUILD_VERSION
    fi
    
  • a data é apenas uma datechamada 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 o nome de host de hostname( KBUILD_BUILD_HOST)

  • A versão do compilador vem gcc -ve parece que não pode ser controlada.

Aqui está como alterar a versão da questão: https://stackoverflow.com/questions/23424174/how-to-customize-or-remove-extra-linux-kernel-version-details-shown-at-boot

Ciro Santilli adicionou uma nova foto
fonte