Da dove uname ottiene le sue informazioni?


40

Da dove uname ottiene davvero le sue informazioni?

Immagino che questo sia qualcosa che dovrebbe essere semplice. Sfortunatamente, non riesco a trovare alcuna intestazione contenente solo tali informazioni.

Supponiamo che qualcuno abbia voluto modificare l'output di base di uname/ uname -s from Linuxin qualcos'altro (essenzialmente, rinominando il kernel).

Come farebbe a farlo nel modo giusto (cioè cambiando la fonte)?

Risposte:


26

L' unameutilità ottiene le informazioni dalla uname()chiamata di sistema. Popola una struttura come questa (vedi 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
       };

Questo deriva direttamente dal kernel in esecuzione. Vorrei assumere tutte le informazioni è hard-coded in esso, tranne forse domainname(e come si scopre, anche nodename, machinee release, vedi commenti). La stringa di rilascio, da uname -r, può essere impostata tramite configurazione al momento della compilazione, ma dubito molto che il campo sysname possa farlo: è il kernel di Linux e non c'è ragione plausibile per usare qualcos'altro.

Tuttavia, dal momento che è open source, è possibile modificare il codice sorgente e ricompilare il kernel per utilizzare qualunque sysname desiderato.


2
Il domainnamecampo è impostato dal domainnamecomando, utilizzando la setdomainnamechiamata di sistema. Allo stesso modo, il nodenamecampo viene impostato dal hostnamecomando, utilizzando la sethostnamechiamata di sistema. (Il valore nodename/ hostnamepuò essere memorizzato in /etc/nodename.)
Scott

2
Questo è irrilevante: la domanda è stata posta dove cambiarlo. Quindi sì, il unamecomando ottiene le sue informazioni da una chiamata di sistema. E da dove la chiamata di sistema ottiene le sue informazioni? (Risposta, fornita da altri poster qui: è codificato nel kernel al momento della compilazione.)
Gilles 'SO- smetti di essere malvagio'

@Gilles: Cosa è irrilevante? Se la risposta è "fornita da altri poster qui: è codificata nel kernel ..." nota ho detto esattamente la stessa cosa: "Questo proviene direttamente dal kernel in esecuzione. Suppongo che tutte le informazioni siano difficili -coded in esso ... dal momento che è open source, è possibile cambiare il codice sorgente e ricompilare il kernel di usare tutto quello che vuoi sysName è. non un'opzione di configurazione.
Goldilocks

2
@goldilocks Perché machinemai cambierebbe? Potrebbe non essere codificato nel kernel perché potrebbe adattarsi all'hardware, ma sicuramente sarebbe impostato al momento dell'avvio e non cambierebbe dopo. Ma no: può essere impostato per processo (ad es. Per riportare i686a 32 bit elaborato su x86_64). A proposito, releasepuò anche essere personalizzato per processo in una certa misura (provare setarch i686 --uname-2.6 uname -a).
Gilles 'SO- smetti di essere malvagio' il

1
@Gilles Ho modificato machine, nodenameerelease nella domanda con un riferimento ai commenti. Ancora una volta, la domanda in realtà non riguardava tutti quei campi.
Riccioli d'oro

26

I dati sono memorizzati in 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);

Le stringhe stesse sono in include / generate / compile.h:

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

e in include / generate / utsrelease.h:

#define UTS_RELEASE "3.14.0-v2-v"

UTS_SYSNAME può essere definito in include / linux / uts.h

#ifndef UTS_SYSNAME
#define UTS_SYSNAME "Linux"
#endif

o come #define nei makefile

Infine, il nome host e il nome dominio possono essere controllati da / proc / sys / kernel / {nomehost, nome dominio}. Questi sono per spazio dei nomi UTS:

# hostname
hell
# unshare --uts /bin/bash
# echo test > /proc/sys/kernel/hostname 
# hostname
test
# exit
# hostname
hell

Questa è generalmente una risposta buona e completa, ma può valere la pena rispondere direttamente alla domanda del poster. Credo che ciò equivarrebbe a: modificare la voce pertinente nel file pertinente e ricompilare. Hai scritto "o come #define nei makefile". Puoi elaborare?
Faheem Mitha,

+1 per unshare. In qualche modo sono riuscito a perdere questo comando fino ad oggi. Grazie!
Tino,

Ed include/generated/compile.hè generato da scripts/mkcompile_h: unix.stackexchange.com/a/485962/32558
Ciro Santilli 新疆 改造 中心 法轮功 六四 事件

8

Con l'aiuto di un riferimento incrociato di Linux e la tua menzione /proc/sys/kernel/ostype, ho tracciato ostypeper includere / linux / sysctl.h , dove un commento dice che i nomi vengono aggiunti chiamando register_sysctl_table.

Quindi da dove viene chiamato ? Un posto è kernel / utsname_sysctl.c , che include include / linux / uts.h , dove troviamo:

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

Quindi, come afferma la documentazione del kernel :

L'unico modo per ottimizzare questi valori è ricostruire il kernel

:-)


6

Come commentato altrove, le informazioni vengono fornite con il uname syscall, le cui informazioni sono codificate nel kernel in esecuzione.

La parte della versione è normalmente impostata quando si compila un nuovo kernel dal Makefile :

VERSION = 3
PATCHLEVEL = 15
SUBLEVEL = 0
EXTRAVERSION =

quando ho avuto il tempo di giocare compilando i miei kernel, aggiungevo cose laggiù in EXTRAVERSION; che ti ha dato uname -r cose del genere3.4.1-mytestkernel .

Non lo capisco del tutto, ma penso che il resto delle informazioni sia impostato Makefileanche nella riga 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

Per il resto dei dati, il sys_unamesyscall viene generato usando le macro (in modo abbastanza contorto), puoi iniziare da qui se ti senti avventuroso.

Probabilmente il modo migliore per cambiare tali informazioni è scrivere un modulo del kernel per sovrascrivere il unamesyscall; Non l'ho mai fatto, ma puoi trovare informazioni in questa pagina alla sezione 4.2 (scusa, nessun link diretto). Si noti tuttavia che quel codice si riferisce a un kernel piuttosto vecchio (ora il kernel Linux ha utsspazi dei nomi, qualunque cosa significino), quindi sarà necessario cambiarlo molto.


Grazie a tutti. Sapevo già che aveva qualcosa a che fare con uname. Tuttavia, ciò che non riesco a capire è come e dove all'interno della sorgente è definita la stringa "Linux". Tutto quello che so è dove posso trovare quelle informazioni durante il runtime (è contenuto in / proc / sys / kernel / ostype). Scoprire come esattamente il kernel stesso sappia che il suo nome proprio sarebbe una delle cose più interessanti, direi.
user237251

@ user237251 quante istanze della parola "Linux" si verificano nel sorgente del kernel in contesti di stringhe? Se non sono così tanti, puoi semplicemente esaminare i risultati di una ricerca testuale e vedere dove ti porta.
JAB,

@JAB Troppi. Fortunatamente, qualcuno su kernelnewbies.org mi ha aiutato a risolvere il "mistero". Linux prende il nome sys da /include/Linux/uts.h. Vedi qui: lxr.free-electrons.com/source/include/linux/uts.h?v=3.10
user237251

2

Sebbene non sia stato possibile trovare nulla nella fonte per indicarlo, credo che utilizzi il syscall uname.

man 2 uname

dovrei dirti di più al riguardo. In questo caso sta ottenendo le informazioni direttamente dal kernel e modificarle probabilmente richiederebbe una ricompilazione.

Potresti cambiare il binario per te uname per fare quello che vuoi, basta scrivere su di esso con il programma w / e per favore. L'aspetto negativo di alcuni script si basa su quell'output.


3
In tal caso strace uname, confermerà che unameviene utilizzata la chiamata di sistema.
Graeme,

1

Il modo corretto di cambiare uname sarebbe quello di cambiare le intestazioni di compilazione e ricompilare come altri hanno suggerito. Ma non sono sicuro del motivo per cui vorresti affrontare così tanti problemi quando puoi fare qualcosa del genere,

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

o anche

alias uname 'echo whatever'

0

La risposta di Rmano mi ha portato a metà strada , ma la vera magia è più facile da scoprire passando l' Q=opzione nella makeriga di comando nella directory dei sorgenti del kernel. ti permette di vedere i dettagli, uno dei quali è una chiamata a uno script: echo "4.4.19$(/bin/sh ./scripts/setlocalversion .)". l'esecuzione di quello stesso frammento dà il numero di versione del kernel, 4.4.19-00010-ge5dddbf. se guardi lo script, determina il numero dal sistema di controllo delle versioni e eseguirlo bash -xmostra il processo esatto:

+++ 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

ciò che mi mostra è che se voglio compilare un modulo del kernel per lavorare con il mio kernel in esecuzione, sto usando la versione taggata sbagliata e il commit sbagliato. Ho bisogno di risolverlo e creare almeno i DTB ( make dtbs) per creare i file generati con il numero di versione corretto.


risulta, anche quello non era abbastanza. Ho dovuto sostituire scripts/setlocalversionquello che semplicemente:

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

quindi ricostruire i file generati automaticamente:

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

allora ho potuto costruire il driver di esempio di Derek Molloy e sono riuscito a insmodfarlo con successo. apparentemente l'avvertimento di Module.symversnon essere presenti non aveva importanza. tutto Linux stava usando per determinare se il modulo avrebbe funzionato era quella stringa di localversion.


0

scripts/mkcompile_h

In v4.19, questo è il file che genera include/generated/compile.he contiene diverse parti interessanti di /proc/version: https://github.com/torvalds/linux/blob/v4.19/scripts/mkcompile_h

  • la #<version>parte proviene dal .versionfile nell'albero della build, che viene incrementato ogni volta che si verifica il collegamento (richiede modifiche al file / alla configurazione) discripts/link-vmlinux.sh .

    Può essere sovrascritto dalla KBUILD_BUILD_VERSIONvariabile d'ambiente:

    if [ -z "$KBUILD_BUILD_VERSION" ]; then
        VERSION=$(cat .version 2>/dev/null || echo 1)
    else
        VERSION=$KBUILD_BUILD_VERSION
    fi
    
  • la data è solo una datechiamata grezza :

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

    e allo stesso modo il nome utente deriva da whoami( KBUILD_BUILD_USER) e il nome host da hostname( KBUILD_BUILD_HOST)

  • La versione del compilatore viene gcc -ve sembra che non possa essere controllata.

Ecco come cambiare la versione roba della domanda: https://stackoverflow.com/questions/23424174/how-to-customize-or-remove-extra-linux-kernel-version-details-shown-at-boot

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.