Skąd uname pobiera informacje?

40

Skąd uname naprawdę czerpie informacje?

Myślę, że to powinno być proste. Niestety nie mogę znaleźć żadnego nagłówka zawierającego tylko te informacje.

Powiedzmy, że ktoś chciał zmienić podstawowe wyjście z uname/ uname -s z Linuxna coś innego (zasadniczo zmieniając nazwę jądra).

Jak miałby to zrobić we właściwy sposób (tj. Zmieniając źródło)?

użytkownik237251
źródło

Odpowiedzi:

26

unameNarzędzie dostaje swoją informację z uname()funkcji systemowej. Wypełnia strukturę taką jak ta (patrz 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
       };

Pochodzi bezpośrednio z działającego jądra. Przypuszczam, wszystkie informacje są zakodowane w nim, z wyjątkiem być może domainname(i jak się okazuje, również nodename, machinei releasepatrz komentarze). Łańcuch zwalniający, z uname -r, można ustawić za pomocą konfiguracji w czasie kompilacji, ale wątpię, czy pole sysname jest w stanie - jest to jądro Linuksa i nie ma żadnego możliwego powodu, aby używać czegoś innego.

Ponieważ jednak jest to oprogramowanie typu open source, można zmienić kod źródłowy i ponownie skompilować jądro, aby użyć dowolnej nazwy systemowej.

Złotowłosa
źródło
2
domainnamePole jest ustawione przez domainnamepolecenia, za pomocą setdomainnamewywołania systemowego. Podobnie nodenamepole jest ustawiane przez hostnamepolecenie za pomocą sethostnamewywołania systemowego. (Wartość nodename/ hostnamemoże być przechowywana w /etc/nodename.)
Scott
2
To nie ma znaczenia - zadane pytanie, gdzie to zmienić. Tak, unamepolecenie otrzymuje informacje z wywołania systemowego. A skąd wywołanie systemowe uzyskuje informacje? (Odpowiedź udzielona przez inne plakaty tutaj: jest mocno zakodowana w jądrze w czasie kompilacji.)
Gilles „SO- przestań być zły”
@Gilles: Co jest nieistotne? Jeśli odpowiedź brzmi „dostarczone przez inne plakaty tutaj: jest zapisana na stałe w jądrze ...” zauważ, że powiedziałem dokładnie to samo: „To pochodzi bezpośrednio z działającego jądra. Zakładam, że wszystkie informacje są trudne -coded do niego ... ponieważ jest open source, można zmienić kod źródłowy i skompilować jądro do użycia cokolwiek sysName chcesz to. nie opcja config.
Złotowłosa
2
@goldilocks Dlaczego miałby się machinezmieniać? Może nie być zakodowany na stałe w jądrze, ponieważ może się dostosować do sprzętu, ale na pewno zostanie ustawiony w czasie uruchamiania i nie zmieni się później. Ale nie: można ustawić na proces (np. Raportować i686do wersji 32-bitowej na x86_64). Nawiasem mówiąc, releasemożna również w pewnym stopniu dostosować do poszczególnych procesów (spróbuj setarch i686 --uname-2.6 uname -a).
Gilles „SO- przestań być zły”
1
@Gilles, które edytowałem machine, nodenamei releasedo pytania z odniesieniem do komentarzy. Znów pytanie nie dotyczyło wszystkich tych dziedzin.
goldilocks
26

Dane są przechowywane w 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);

Same łańcuchy znajdują się w plikach include / Wygenerowane / Kompilacja. H:

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

oraz w include / wygenerowane / utsrelease.h:

#define UTS_RELEASE "3.14.0-v2-v"

UTS_SYSNAME może być zdefiniowany w include / linux / uts.h

#ifndef UTS_SYSNAME
#define UTS_SYSNAME "Linux"
#endif

lub jako #define w makefiles

Na koniec nazwa hosta i nazwa domeny mogą być kontrolowane przez / proc / sys / kernel / {nazwa hosta, nazwa domeny}. Są to według przestrzeni nazw UTS:

# hostname
hell
# unshare --uts /bin/bash
# echo test > /proc/sys/kernel/hostname 
# hostname
test
# exit
# hostname
hell
V13
źródło
Jest to ogólnie dobra i kompletna odpowiedź, ale może być warto, odpowiadając bezpośrednio na pytanie plakatu. Wierzę, że sprowadzałoby się to do - zmiany odpowiedniego wpisu w odpowiednim pliku i ponownej kompilacji. Napisałeś „lub jako #define w makefiles”. Czy możesz rozwinąć?
Faheem Mitha
+1 dla unshare. Jakoś udało mi się przegapić to polecenie do dziś. Dzięki!
Tino
I include/generated/compile.hjest generowany przez scripts/mkcompile_h: unix.stackexchange.com/a/485962/32558
Ciro Santilli 新疆 改造 中心 法轮功 六四 事件
8

Z pomocą Linux Odsyłacze i swojej wzmianki /proc/sys/kernel/ostype, że śledzone ostypedo include / linux / sysctl.h , gdzie komentarz mówi, że nazwy są dodawane przez wywołanie register_sysctl_table.

Więc gdzie jest to nazywane od ? Jednym z nich jest jądro / utsname_sysctl.c , które obejmuje include / linux / uts.h , gdzie znajdujemy:

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

Tak więc, jak stwierdza dokumentacja jądra :

Jedynym sposobem dostrojenia tych wartości jest przebudowanie jądra

:-)

deltab
źródło
6

Jak skomentowano w innym miejscu, informacje pochodzą z unamesyscall, który jest zakodowany na stałe w uruchomionym jądrze.

Część wersji jest zwykle ustawiana podczas kompilacji nowego jądra przez Makefile :

VERSION = 3
PATCHLEVEL = 15
SUBLEVEL = 0
EXTRAVERSION =

kiedy miałem czas na zabawę w kompilowanie jądra, dodawałem tam rzeczy w EXTRAVERSION; które dały ci uname -r takie rzeczy jak 3.4.1-mytestkernel.

Nie do końca to rozumiem, ale myślę, że reszta informacji znajduje się Makefilerównież w wierszu 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

W przypadku pozostałych danych sys_unamesyscall jest generowany za pomocą makr (w dość skomplikowany sposób), możesz zacząć od tego miejsca, jeśli czujesz się ryzykowny.

Prawdopodobnie najlepszym sposobem na zmianę takich informacji jest napisanie modułu jądra, aby zastąpić unamesyscall; Nigdy tego nie zrobiłem, ale informacje można znaleźć na tej stronie w punkcie 4.2 (przepraszam, brak bezpośredniego linku). Zauważ jednak, że ten kod odnosi się do dość starego jądra (teraz jądro Linuksa ma utsprzestrzenie nazw, cokolwiek mają na myśli), więc prawdopodobnie będziesz musiał je zmienić.

Rmano
źródło
Dziękuję wszystkim. Wiedziałem już, że ma to coś wspólnego z Uname. Jednak nie mogę pojąć, jak i gdzie w źródle jest zdefiniowany ciąg „Linux”. Wiem tylko, gdzie mogę znaleźć te informacje podczas uruchamiania (są one zawarte w katalogu / proc / sys / kernel / ostype). Dowiedziałbym się, że sprawdzenie, jak dokładnie samo jądro wie, że jego właściwa nazwa byłaby jedną z bardziej interesujących rzeczy.
user237251
@ user237251 ile wystąpień słowa „Linux” występuje w źródle jądra w kontekście łańcuchowym? Jeśli nie jest ich tak wiele, możesz po prostu sprawdzić wyniki wyszukiwania tekstowego i zobaczyć, dokąd cię to zaprowadzi.
JAB
@JAB Zbyt wiele. Na szczęście ktoś na kernelnewbies.org pomógł mi rozwiązać „zagadkę”. Linux otrzymuje nazwę sys od /include/Linux/uts.h. Zobacz tutaj: lxr.free-electrons.com/source/include/linux/uts.h?v=3.10
user237251
2

Chociaż nie mogłem znaleźć w źródle niczego, co by to wskazywało, sądzę, że używa on połączenia systemowego uname.

man 2 uname

powinienem ci więcej o tym powiedzieć. W takim przypadku pobieranie informacji bezpośrednio z jądra i ich zmiana prawdopodobnie wymagałoby ponownej kompilacji.

Możesz zmienić plik binarny, aby nie robić nic, co chcesz, po prostu napisz go za pomocą w / e programu, który chcesz. Minusem jest to, że niektóre skrypty polegają na tych wynikach.

Livinglifeback
źródło
3
Jeśli to zrobisz strace uname, potwierdzi unameto użycie połączenia systemowego.
Graeme
1

Właściwym sposobem zmiany uname byłaby zmiana nagłówków kompilacji i ponowna kompilacja, jak sugerują inni. Ale nie jestem pewien, dlaczego miałbyś mieć tyle problemów, skoro możesz zrobić coś takiego,

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

lub nawet

alias uname 'echo whatever'
Saurabh
źródło
0

Odpowiedź Rmano w pewnym stopniu mnie wciągnęła, ale prawdziwą magię łatwiej odkryć, przekazując Q=opcję w linii makepoleceń w katalogu źródłowym jądra. To pozwala zobaczyć szczegóły, z których jeden jest wezwaniem do skryptu: echo "4.4.19$(/bin/sh ./scripts/setlocalversion .)". wykonujące ten sam fragment podaje numer wersji jądra, 4.4.19-00010-ge5dddbf. jeśli spojrzysz na skrypt, określa on liczbę z systemu kontroli wersji, a uruchomienie go z bash -xpokazuje dokładny proces:

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

pokazuje mi to, że jeśli chcę zbudować moduł jądra do pracy z działającym jądrem, mam niewłaściwie oznakowane wydanie i niewłaściwe zatwierdzenie. Muszę to naprawić i zbudować co najmniej DTB ( make dtbs), aby utworzyć wygenerowane pliki z odpowiednim numerem wersji.


okazuje się, że to nie wystarczyło. Musiałem wymienić scripts/setlocalversionna taki, który po prostu robi:

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

następnie odbuduj automatycznie wygenerowane pliki:

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

wtedy mogłem zbudować przykładowy sterownik Dereka Molloya i udało mi się insmodto z powodzeniem. najwyraźniej ostrzeżenie o Module.symversnieobecności nie miało znaczenia. cały Linux używał do ustalenia, czy moduł będzie działał, był tym ciągiem wersji lokalnej.

jcomeau_ictx
źródło
0

scripts/mkcompile_h

W wersji 4.19 jest to plik, który generuje include/generated/compile.hi zawiera kilka interesujących części /proc/version: https://github.com/torvalds/linux/blob/v4.19/scripts/mkcompile_h

  • #<version>część pochodzi z .versionpliku na drzewie build, który zostanie zwiększany, gdy związek się dzieje (wymaga pliku / config zmianami), których autorem scripts/link-vmlinux.sh.

    Można go zastąpić KBUILD_BUILD_VERSIONzmienną środowiskową:

    if [ -z "$KBUILD_BUILD_VERSION" ]; then
        VERSION=$(cat .version 2>/dev/null || echo 1)
    else
        VERSION=$KBUILD_BUILD_VERSION
    fi
    
  • data jest tylko surowym datepołączeniem:

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

    i podobnie nazwa użytkownika pochodzi od whoami( KBUILD_BUILD_USER) i nazwa hosta od hostname( KBUILD_BUILD_HOST)

  • Wydaje się, że wersja kompilatora pochodzi gcc -vi nie można jej kontrolować.

Oto jak zmienić rzeczową wersję pytania: https://stackoverflow.com/questions/23424174/how-to-customize-or-remove-extra-linux-kernel-version-details-shown-at-boot

Ciro Santilli
źródło