mirror of
https://github.com/HorlogeSkynet/archey4
synced 2025-04-19 04:00:13 +02:00

* Fixes bad mocked return values * Implements missing test cases for `LAN_IP` (in a separate module) * Adds missing test case with `emerge` packages tool
1082 lines
42 KiB
Python
1082 lines
42 KiB
Python
#!/usr/bin/env python3
|
|
|
|
|
|
import json
|
|
import os
|
|
import re
|
|
import sys
|
|
from enum import Enum
|
|
from glob import glob
|
|
from os import getenv, getuid
|
|
from subprocess import CalledProcessError, DEVNULL, PIPE, Popen, \
|
|
TimeoutExpired, check_output
|
|
|
|
|
|
# ----- Distributions fingerprints ---- #
|
|
|
|
class Distributions(Enum):
|
|
ARCH_LINUX = 'Arch.*'
|
|
BUNSENLABS = 'BunsenLabs'
|
|
CRUNCHBANG = 'CrunchBang'
|
|
DEBIAN = '(Rasp|De)bian'
|
|
FEDORA = 'Fedora'
|
|
GENTOO = 'Gentoo'
|
|
KALI_LINUX = 'Kali'
|
|
MANJARO_LINUX = 'ManjaroLinux'
|
|
LINUX = 'Linux'
|
|
LINUX_MINT = 'LinuxMint'
|
|
OPENSUSE = 'openSUSE'
|
|
RED_HAT = 'Red Hat'
|
|
UBUNTU = 'Ubuntu'
|
|
WINDOWS = 'Windows'
|
|
|
|
|
|
# ------------ Dictionaries ----------- #
|
|
|
|
colorDict = {
|
|
Distributions.ARCH_LINUX: ['\x1b[0;34m', '\x1b[1;34m'],
|
|
Distributions.BUNSENLABS: ['\x1b[1;37m', '\x1b[1;33m', '\x1b[0;33m'],
|
|
Distributions.CRUNCHBANG: ['\x1b[1;37m', '\x1b[1;37m'],
|
|
Distributions.DEBIAN: ['\x1b[0;31m', '\x1b[1;31m'],
|
|
Distributions.FEDORA: ['\x1b[1;37m', '\x1b[0;34m'],
|
|
Distributions.GENTOO: ['\x1b[1;37m', '\x1b[1;35m'],
|
|
Distributions.KALI_LINUX: ['\x1b[1;37m', '\x1b[1;34m'],
|
|
Distributions.MANJARO_LINUX: ['\x1b[0;32m', '\x1b[1;32m'],
|
|
Distributions.LINUX: ['\x1b[1;33m', '\x1b[1;37m'],
|
|
Distributions.LINUX_MINT: ['\x1b[1;37m', '\x1b[1;32m'],
|
|
Distributions.OPENSUSE: ['\x1b[1;37m', '\x1b[1;32m'],
|
|
Distributions.RED_HAT: ['\x1b[1;37m', '\x1b[1;31m', '\x1b[0;31m'],
|
|
Distributions.UBUNTU: ['\x1b[0;31m', '\x1b[1;31m', '\x1b[0;33m'],
|
|
Distributions.WINDOWS: ['\x1b[1;31m', '\x1b[1;34m',
|
|
'\x1b[1;32m', '\x1b[0;33m'],
|
|
'sensors': ['\x1b[0;32m', '\x1b[0;33m', '\x1b[0;31m'],
|
|
'clear': '\x1b[0m'
|
|
}
|
|
|
|
deDict = {
|
|
'cinnamon': 'Cinnamon',
|
|
'gnome-session': 'GNOME',
|
|
'gnome-shell': 'GNOME',
|
|
'mate-session': 'MATE',
|
|
'ksmserver': 'KDE',
|
|
'xfce4-session': 'Xfce',
|
|
'fur-box-session': 'Fur Box',
|
|
'lxsession': 'LXDE',
|
|
'lxqt-session': 'LXQt'
|
|
}
|
|
|
|
wmDict = {
|
|
'awesome': 'Awesome',
|
|
'beryl': 'Beryl',
|
|
'blackbox': 'Blackbox',
|
|
'bspwm': 'bspwm',
|
|
'cinnamon': 'Cinnamon',
|
|
'compiz': 'Compiz',
|
|
'dwm': 'DWM',
|
|
'enlightenment': 'Enlightenment',
|
|
'herbstluftwm': 'herbstluftwm',
|
|
'fluxbox': 'Fluxbox',
|
|
'fvwm': 'FVWM',
|
|
'i3': 'i3',
|
|
'icewm': 'IceWM',
|
|
'kwin_x11': 'KWin',
|
|
'metacity': 'Metacity',
|
|
'musca': 'Musca',
|
|
'openbox': 'Openbox',
|
|
'pekwm': 'PekWM',
|
|
'qtile': 'QTile',
|
|
'ratpoison': 'ratpoison',
|
|
'scrotwm': 'ScrotWM',
|
|
'stumpwm': 'StumpWM',
|
|
'subtle': 'Subtle',
|
|
'monsterwm': 'MonsterWM',
|
|
'wingo': 'Wingo',
|
|
'wmaker': 'Window Maker',
|
|
'wmfs': 'Wmfs',
|
|
'wmii': 'wmii',
|
|
'xfwm4': 'Xfwm',
|
|
'xmonad': 'xmonad'
|
|
}
|
|
|
|
logosDict = {
|
|
Distributions.ARCH_LINUX: """
|
|
{c[1]} + {r[0]}
|
|
{c[1]} # {r[1]}
|
|
{c[1]} ### {r[2]}
|
|
{c[1]} ##### {r[3]}
|
|
{c[1]} ###### {r[4]}
|
|
{c[1]} ; #####; {r[5]}
|
|
{c[1]} +##.##### {r[6]}
|
|
{c[1]} +########## {r[7]}
|
|
{c[1]} ######{c[0]}#####{c[1]}##; {r[8]}
|
|
{c[1]} ###{c[0]}############{c[1]}+ {r[9]}
|
|
{c[1]} #{c[0]}###### #######{c[1]} {r[10]}
|
|
{c[1]} {c[0]}.######; ;###;`\".{c[1]} {r[11]}
|
|
{c[1]} {c[0]}.#######; ;#####.{c[1]} {r[12]}
|
|
{c[1]} {c[0]}#########. .########`{c[1]} {r[13]}
|
|
{c[1]} {c[0]}######' '######{c[1]} {r[14]}
|
|
{c[1]} {c[0]};#### ####;{c[1]} {r[15]}
|
|
{c[1]} {c[0]}##' '##{c[1]} {r[16]}
|
|
{c[1]} {c[0]}#' `#{c[1]} {r[17]}\n""",
|
|
Distributions.BUNSENLABS: """
|
|
{c[0]} .{c[1]}..{c[0]}+hhy+-` \0
|
|
{c[0]} `+hd{c[1]}-{c[0]}+dddd{c[2]}hyso{c[0]}+//: \0
|
|
{c[0]} `+dddd{c[1]}:-{c[0]}sdh/. {r[0]}
|
|
{c[0]} -hdddddh{c[1]}-.{c[2]}/:{c[0]} {r[1]}
|
|
{c[0]} /ddddddddd{c[1]}:```{c[0]} {r[2]}
|
|
{c[0]} :ddddddddddd/ {r[3]}
|
|
{c[0]} `hdddddddddddd+ {r[4]}
|
|
{c[0]} /dddddddddddddd: {r[5]}
|
|
{c[0]} odddds..sddddddh {r[6]}
|
|
{c[0]} oddd/ /dddddd: {r[7]}
|
|
{c[0]} +dd+ +ddddd+ {r[8]}
|
|
{c[0]} .dd` `ddddd+ {r[9]}
|
|
{c[0]} oh ydddd: {r[10]}
|
|
{c[0]} `o sdddh` {r[11]}
|
|
{c[0]} yddd: {r[12]}
|
|
{c[0]} `dddo {r[13]}
|
|
{c[0]} :s :dds {r[14]}
|
|
{c[0]} yd/ yd+ {r[15]}
|
|
{c[0]} `sddy :h- {r[16]}
|
|
{c[0]} `sddys` :` {r[17]}
|
|
{c[0]} -hdy+`y+yo./+/:- \0
|
|
{c[0]} ... .o++oso+/ \n""",
|
|
Distributions.CRUNCHBANG: """
|
|
{c[0]} ___ ___ _ {r[0]}
|
|
{c[0]} / / / / | | {r[1]}
|
|
{c[0]} / / / / | | {r[2]}
|
|
{c[0]} / / / / | | {r[3]}
|
|
{c[0]} _______/ /______/ /______ | | {r[4]}
|
|
{c[0]} /______ _______ _______/ | | {r[5]}
|
|
{c[0]} / / / / | | {r[6]}
|
|
{c[0]} / / / / | | {r[7]}
|
|
{c[0]} / / / / | | {r[8]}
|
|
{c[0]} ______/ /______/ /______ | | {r[9]}
|
|
{c[0]} /_____ _______ _______/ | | {r[10]}
|
|
{c[0]} / / / / | | {r[11]}
|
|
{c[0]} / / / / |_| {r[12]}
|
|
{c[0]} / / / / _ {r[13]}
|
|
{c[0]} / / / / | | {r[14]}
|
|
{c[0]} /__/ /__/ |_| {r[15]}
|
|
{c[0]} {r[16]}
|
|
{c[0]} {r[17]}\n""",
|
|
Distributions.DEBIAN: """
|
|
{c[1]} {r[0]}
|
|
{c[1]} _sudZUZ#Z#XZo=_ {r[1]}
|
|
{c[1]} _jmZZ2!!~---~!!X##wx {r[2]}
|
|
{c[1]} .<wdP~~ -!YZL, {r[3]}
|
|
{c[1]} .mX2' _xaaa__ XZ[. {r[4]}
|
|
{c[1]} oZ[ _jdXY!~?S#wa ]Xb; {r[5]}
|
|
{c[1]} _#e' .]X2( ~Xw| )XXc {r[6]}
|
|
{c[1]} .2Z` ]X[. xY| ]oZ( {r[7]}
|
|
{c[1]} .2#; )3k; _s!~ jXf` {r[8]}
|
|
{c[1]} {c[0]}1Z>{c[1]} {c[0]}-]Xb/{c[1]} {c[0]}~{c[1]} {c[0]}__#2({c[1]} {r[9]}
|
|
{c[1]} {c[0]}-Zo;{c[1]} {c[0]}+!4ZwerfgnZZXY'{c[1]} {r[10]}
|
|
{c[1]} {c[0]}*#[,{c[1]} {c[0]}~-?!!!!!!-~{c[1]} {r[11]}
|
|
{c[1]} {c[0]}XUb;.{c[1]} {r[12]}
|
|
{c[1]} {c[0]})YXL,,{c[1]} {r[13]}
|
|
{c[1]} {c[0]}+3#bc,{c[1]} {r[14]}
|
|
{c[1]} {c[0]}-)SSL,,{c[1]} {r[15]}
|
|
{c[1]} {c[0]}~~~~~{c[1]} {r[16]}
|
|
{c[1]} {r[17]}\n""",
|
|
Distributions.FEDORA: """
|
|
{c[1]} :/------------:// {r[0]}
|
|
{c[1]} :------------------:// {r[1]}
|
|
{c[1]} :-----------{c[0]}/shhdhyo/{c[1]}-:// {r[2]}
|
|
{c[1]} /-----------{c[0]}omMMMNNNMMMd/{c[1]}-:/ {r[3]}
|
|
{c[1]} :-----------{c[0]}sMMMdo:/{c[1]} -:/ {r[4]}
|
|
{c[1]} :-----------{c[0]}:MMMd{c[1]}------- --:/ {r[5]}
|
|
{c[1]} /-----------{c[0]}:MMMy{c[1]}------- ---/ {r[6]}
|
|
{c[1]} :------ --{c[0]}/+MMMh/{c[1]}-- ---: {r[7]}
|
|
{c[1]} :--- {c[0]}oNMMMMMMMMMNho{c[1]} -----: {r[8]}
|
|
{c[1]} :-- {c[0]}+shhhMMMmhhy++{c[1]} ------: {r[9]}
|
|
{c[1]} :- -----{c[0]}:MMMy{c[1]}--------------/ {r[10]}
|
|
{c[1]} :- ------{c[0]}/MMMy{c[1]}-------------: {r[11]}
|
|
{c[1]} :- ----{c[0]}/hMMM+{c[1]}------------: {r[12]}
|
|
{c[1]} :--{c[0]}:dMMNdhhdNMMNo{c[1]}-----------: {r[13]}
|
|
{c[1]} :---{c[0]}:sdNMMMMNds:{c[1]}----------: {r[14]}
|
|
{c[1]} :------{c[0]}:://:{c[1]}-----------:// {r[15]}
|
|
{c[1]} :--------------------:// {r[16]}
|
|
{c[1]} {r[17]}\n""",
|
|
Distributions.GENTOO: """
|
|
{c[1]} -/oyddmdhs+:. {r[0]}
|
|
{c[1]} -o{c[0]}dNMMMMMMMMNNmhy+{c[1]}-` {r[1]}
|
|
{c[1]} -y{c[0]}NMMMMMMMMMMMNNNmmdhy{c[1]}+- {r[2]}
|
|
{c[1]} `o{c[0]}mMMMMMMMMMMMMNmdmmmmddhhy{c[1]}/` {r[3]}
|
|
{c[1]} om{c[0]}MMMMMMMMMMMN{c[1]}hhyyyo{c[0]}hmdddhhhd{c[1]}o` {r[4]}
|
|
{c[1]} .y{c[0]}dMMMMMMMMMMd{c[1]}hs++so/s{c[0]}mdddhhhhdm{c[1]}+` {r[5]}
|
|
{c[1]} oy{c[0]}hdmNMMMMMMMN{c[1]}dyooy{c[0]}dmddddhhhhyhN{c[1]}d. {r[6]}
|
|
{c[1]} :o{c[0]}yhhdNNMMMMMMMNNNmmdddhhhhhyym{c[1]}Mh {r[7]}
|
|
{c[1]} .:{c[0]}+sydNMMMMMNNNmmmdddhhhhhhmM{c[1]}my {r[8]}
|
|
{c[1]} /m{c[0]}MMMMMMNNNmmmdddhhhhhmMNh{c[1]}s: {r[9]}
|
|
{c[1]} `o{c[0]}NMMMMMMMNNNmmmddddhhdmMNhs{c[1]}+` {r[10]}
|
|
{c[1]} `s{c[0]}NMMMMMMMMNNNmmmdddddmNMmhs{c[1]}/. {r[11]}
|
|
{c[1]} /N{c[0]}MMMMMMMMNNNNmmmdddmNMNdso{c[1]}:` {r[12]}
|
|
{c[1]} +M{c[0]}MMMMMMNNNNNmmmmdmNMNdso{c[1]}/- {r[13]}
|
|
{c[1]} yM{c[0]}MNNNNNNNmmmmmNNMmhs+/{c[1]}-` {r[14]}
|
|
{c[1]} /h{c[0]}MMNNNNNNNNMNdhs++/{c[1]}-` {r[15]}
|
|
{c[1]} `/{c[0]}ohdmmddhys+++/:{c[1]}.` {r[16]}
|
|
{c[1]} `-//////:--. {r[17]}\n""",
|
|
Distributions.KALI_LINUX: """
|
|
{c[0]} ,..... \0
|
|
{c[0]} ----` `..,;:ccc,. \0
|
|
{c[0]} ......''';lxO. {r[0]}
|
|
{c[0]} .....''''..........,:ld; {r[1]}
|
|
{c[0]} .';;;:::;,,.x, {r[2]}
|
|
{c[0]} ..'''. 0Xxoc:,. ... {r[3]}
|
|
{c[0]} .... ,ONkc;,;cokOdc',. {r[4]}
|
|
{c[0]} . OMo ':{c[1]}d{c[0]}o. {r[5]}
|
|
{c[0]} dMc :OO; {r[6]}
|
|
{c[0]} 0M. .:o. {r[7]}
|
|
{c[0]} ;Wd {r[8]}
|
|
{c[0]} ;XO, {r[9]}
|
|
{c[0]} ,d0Odlc;,.. {r[10]}
|
|
{c[0]} ..',;:cdOOd::,. {r[11]}
|
|
{c[0]} .:d;.':;. {r[12]}
|
|
{c[0]} 'd, .' {r[13]}
|
|
{c[0]} ;l .. {r[14]}
|
|
{c[0]} .o {r[15]}
|
|
{c[0]} c {r[16]}
|
|
{c[0]} .' {r[17]}
|
|
{c[0]} . \n""",
|
|
Distributions.MANJARO_LINUX: """
|
|
{c[0]} $$$$$$$$$$$$$$$$ $$$$$$$ {r[0]}
|
|
{c[0]} M77777777777777M M77777M {r[1]}
|
|
{c[0]} M77777777777777M M77777M {r[2]}
|
|
{c[0]} M77777MMMMMMMMMM M77777M {r[3]}
|
|
{c[0]} M77777M M77777M {r[4]}
|
|
{c[0]} M77777M $$$$$$$ M77777M {r[5]}
|
|
{c[0]} MMMMMMM M77777M M77777M {r[6]}
|
|
{c[0]} M77777M M77777M {r[7]}
|
|
{c[0]} $$$$$$$ M77777M M77777M {r[8]}
|
|
{c[0]} M77777M M77777M M77777M {r[9]}
|
|
{c[0]} M77777M M77777M M77777M {r[10]}
|
|
{c[0]} M77777M M77777M M77777M {r[11]}
|
|
{c[0]} M77777M M77777M M77777M {r[12]}
|
|
{c[0]} M77777M M77777M M77777M {r[13]}
|
|
{c[0]} M77777M M77777M M77777M {r[14]}
|
|
{c[0]} M77777M M77777M M77777M {r[15]}
|
|
{c[0]} M77777M M77777M M77777M {r[16]}
|
|
{c[0]} MMMMMMM MMMMMMM MMMMMMM {r[17]}\n""",
|
|
Distributions.LINUX: """
|
|
{c[1]} {r[0]}
|
|
{c[1]} a8888b. {r[1]}
|
|
{c[1]} d888888b. {r[2]}
|
|
{c[1]} 8P"YP"Y88 {r[3]}
|
|
{c[1]} 8|o||o|88 {r[4]}
|
|
{c[1]} 8{c[0]}\\vvvv/{c[1]}88 {r[5]}
|
|
{c[1]} 8{c[0]} \\vv/ {c[1]}Y8. {r[6]}
|
|
{c[1]} d/ {c[0]}`'{c[1]} \8b. {r[7]}
|
|
{c[1]} .dP . Y8b. {r[8]}
|
|
{c[1]} d8:' " `::88b. {r[9]}
|
|
{c[1]} d8" `Y88b {r[10]}
|
|
{c[1]} :8P ' :888 {r[11]}
|
|
{c[1]} 8a. : _a88P {r[12]}
|
|
{c[1]} {c[0]}._/"{c[1]}Yaa_ : .{c[0]}| {c[1]}88P{c[0]}|{c[1]} {r[13]}
|
|
{c[1]} {c[0]}\++++{c[1]}YP" `{c[0]}| {c[1]}8P{c[0]}++\.{c[1]} {r[14]}
|
|
{c[1]} {c[0]}/+++++\.{c[1]}_____.d{c[0]}|+++++/{c[1]} {r[15]}
|
|
{c[1]} {c[0]}\++++++){c[1]}888888P{c[0]}\+++/{c[1]} {r[16]}
|
|
{c[1]} {r[17]}\n""",
|
|
Distributions.LINUX_MINT: """
|
|
{c[1]} {r[0]}
|
|
{c[1]} MMMMMMMMMMMMMMMMMMMMMMMMMmds+. {r[1]}
|
|
{c[1]} MMm----::-://////////////oymNMd+` {r[2]}
|
|
{c[1]} MMd {c[0]}/++{c[1]} -sNMd: {r[3]}
|
|
{c[1]} MMNso/` {c[0]}dMM{c[1]} {c[0]}`.::-. .-::.`{c[1]} .hMN: {r[4]}
|
|
{c[1]} ddddMMh {c[0]}dMM{c[1]} {c[0]}:hNMNMNhNMNMNh:`{c[1]} NMm {r[5]}
|
|
{c[1]} NMm {c[0]}dMM{c[1]} {c[0]}.NMN/-+MMM+-/NMN`{c[1]} dMM {r[6]}
|
|
{c[1]} NMm {c[0]}dMM{c[1]} {c[0]}-MMm{c[1]} {c[0]}`MMM{c[1]} {c[0]}dMM.{c[1]} dMM {r[7]}
|
|
{c[1]} NMm {c[0]}dMM{c[1]} {c[0]}-MMm{c[1]} {c[0]}`MMM{c[1]} {c[0]}dMM.{c[1]} dMM {r[8]}
|
|
{c[1]} NMm {c[0]}dMM{c[1]} {c[0]}.mmd{c[1]} {c[0]}`mmm{c[1]} {c[0]}yMM.{c[1]} dMM {r[9]}
|
|
{c[1]} NMm {c[0]}dMM`{c[1]} {c[0]}..`{c[1]} {c[0]}`...{c[1]} {c[0]}ydm.{c[1]} dMM {r[10]}
|
|
{c[1]} hMM- {c[0]}+MMd/-------...-:sdds{c[1]} MMM {r[11]}
|
|
{c[1]} -NMm- {c[0]}:hNMNNNmdddddddddy/`{c[1]} dMM {r[12]}
|
|
{c[1]} -dMNs-``{c[0]}-::::-------.``{c[1]} dMM {r[13]}
|
|
{c[1]} `/dMNmy+/:-------------:/yMMM {r[14]}
|
|
{c[1]} ./ydNMMMMMMMMMMMMMMMMMMMMM {r[15]}
|
|
{c[1]} {r[16]}
|
|
{c[1]} {r[17]}\n""",
|
|
Distributions.OPENSUSE: """
|
|
{c[0]} .;ldkO0000Okdl;. {r[0]}
|
|
{c[0]} .;d00xl:^''''''^:ok00d;. {r[1]}
|
|
{c[0]} .d00l' 'o00d. {r[2]}
|
|
{c[0]} .d0Kd' {c[1]}Okxol:;,.{c[0]} :O0d. {r[3]}
|
|
{c[0]} .OK{c[1]}KKK0kOKKKKKKKKKKOxo:,{c[0]} lKO. {r[4]}
|
|
{c[0]} ,0K{c[1]}KKKKKKKKKKKKKKK0P^{c[0]},,,{c[1]}^dx:{c[0]} ;00, {r[5]}
|
|
{c[0]} .OK{c[1]}KKKKKKKKKKKKKKKk'{c[0]}.oOPPb.{c[1]}'0k.{c[0]} cKO. {r[6]}
|
|
{c[0]} :KK{c[1]}KKKKKKKKKKKKKKK: {c[0]}kKx..dd{c[1]} lKd{c[0]} 'OK: {r[7]}
|
|
{c[0]} dKK{c[1]}KKKKKKKKKOx0KKKd {c[0]}^0KKKO'{c[1]} kKKc{c[0]} dKd {r[8]}
|
|
{c[0]} dKK{c[1]}KKKKKKKKKK;.;oOKx,..{c[0]}^{c[1]}..;kKKK0.{c[0]} dKd {r[9]}
|
|
{c[0]} :KK{c[1]}KKKKKKKKKK0o;...^cdxxOK0O/^^'{c[0]} .0K: {r[10]}
|
|
{c[0]} kKK{c[1]}KKKKKKKKKKKKK0x;,,......,;od{c[0]} lKk {r[11]}
|
|
{c[0]} '0K{c[1]}KKKKKKKKKKKKKKKKKKKK00KKOo^{c[0]} c00' {r[12]}
|
|
{c[0]} 'kK{c[1]}KKOxddxkOO00000Okxoc;''{c[0]} .dKk' {r[13]}
|
|
{c[0]} l0Ko. .c00l' {r[14]}
|
|
{c[0]} 'l0Kk:. .;xK0l' {r[15]}
|
|
{c[0]} 'lkK0xl:;,,,,;:ldO0kl' {r[16]}
|
|
{c[0]} '^:ldxkkkkxdl:^' {r[17]}\n""",
|
|
Distributions.RED_HAT: """
|
|
{c[1]} {r[0]}
|
|
{c[1]} {c[2]}\`.-..........\`{c[1]} {r[1]}
|
|
{c[1]} {c[2]}\`////////::.\`-/.{c[1]} {r[2]}
|
|
{c[1]} {c[2]}-: ....-////////.{c[1]} {r[3]}
|
|
{c[1]} {c[2]}//:-::///////////\`{c[1]} {r[4]}
|
|
{c[1]} {c[2]}\`--::: \`-://////////////:{c[1]} {r[5]}
|
|
{c[1]} {c[2]}//////- \`\`.-://///////{c[1]} .\` {r[6]}
|
|
{c[1]} {c[2]}\`://////:-.\` :///////::///:\`{c[1]} {r[7]}
|
|
{c[1]} {c[2]}.-/////////:---/////////////:{c[1]} {r[8]}
|
|
{c[1]} {c[2]}.-://////////////////////.{c[1]} {r[9]}
|
|
{c[1]} {c[0]}yMN+\`.-${c[2]}::///////////////-\`{c[1]} {r[10]}
|
|
{c[1]} {c[0]}.-\`:NMMNMs\` \`..-------..\`{c[1]} {r[11]}
|
|
{c[1]} {c[0]}MN+/mMMMMMhoooyysshsss{c[1]} {r[12]}
|
|
{c[1]} {c[0]}MMM MMMMMMMMMMMMMMyyddMMM+{c[1]} {r[13]}
|
|
{c[1]} {c[0]}MMMM MMMMMMMMMMMMMNdyNMMh\` hyhMMM{c[1]} {r[14]}
|
|
{c[1]} {c[0]}MMMMMMMMMMMMMMMMyoNNNMMM+. MMMMMMMM{c[1]} {r[15]}
|
|
{c[1]} {c[0]}MMNMMMNNMMMMMNM+ mhsMNyyyyMNMMMMsMM{c[1]} {r[16]}
|
|
{c[1]} {r[17]}\n""",
|
|
Distributions.UBUNTU: """
|
|
{c[1]} {c[0]}.oyhhs:{c[1]} {r[0]}
|
|
{c[1]} ..--.., {c[0]}shhhhhh-{c[1]} {r[1]}
|
|
{c[1]} -+++++++++`:{c[0]}yyhhyo`{c[1]} {r[2]}
|
|
{c[1]} {c[2]}.--{c[1]} -++++++++/-.-{c[0]}::-`{c[1]} {r[3]}
|
|
{c[1]} {c[2]}.::::-{c[1]} :-----:/+++/++/. {r[4]}
|
|
{c[1]} {c[2]}-:::::-.{c[1]} .:++++++: {r[5]}
|
|
{c[1]} ,,, {c[2]}.:::::-`{c[1]} .++++++- {r[6]}
|
|
{c[1]} ./+++/-{c[2]}`-::-{c[1]} ./////: {r[7]}
|
|
{c[1]} +++++++ {c[2]}.::-{c[1]} {r[8]}
|
|
{c[1]} ./+++/-`{c[2]}-::-{c[1]} {c[0]}:yyyyyo{c[1]} {r[9]}
|
|
{c[1]} ``` `{c[2]}-::::-`{c[1]} {c[0]}:yhhhhh:{c[1]} {r[10]}
|
|
{c[1]} {c[2]}-:::::-.{c[1]} {c[0]}`-ohhhhhh+{c[1]} {r[11]}
|
|
{c[1]} {c[2]}.::::-`{c[1]} {c[0]}-o+///+oyhhyyyhy:{c[1]} {r[12]}
|
|
{c[1]} {c[2]}`.--{c[1]} {c[0]}/yhhhhhhhy+{c[2]},....{c[1]} {r[13]}
|
|
{c[1]} {c[0]}/hhhhhhhhh{c[2]}-.-:::;{c[1]} {r[14]}
|
|
{c[1]} {c[0]}`.:://::- {c[2]}-:::::;{c[1]} {r[15]}
|
|
{c[1]} {c[2]}`.-:-'{c[1]} {r[16]}
|
|
{c[1]} {r[17]}\n""",
|
|
Distributions.WINDOWS: """
|
|
{c[1]} {r[0]}
|
|
{c[1]} {c[0]},.=:^!^!t3Z3z.,{c[1]} {r[1]}
|
|
{c[1]} {c[0]}:tt:::tt333EE3{c[1]} {r[2]}
|
|
{c[1]} {c[0]}Et:::ztt33EEE{c[1]} {c[2]}@Ee.,{c[1]} {c[2]}..,{c[1]} {r[3]}
|
|
{c[1]} {c[0]};tt:::tt333EE7{c[1]} {c[2]};EEEEEEttttt33#{c[1]} {r[4]}
|
|
{c[1]} {c[0]}:Et:::zt333EEQ.{c[1]} {c[2]}SEEEEEttttt33QL{c[1]} {r[5]}
|
|
{c[1]} {c[0]}it::::tt333EEF{c[1]} {c[2]}@EEEEEEttttt33F{c[1]} {r[6]}
|
|
{c[1]} {c[0]};3=*^```'*4EEV{c[1]} {c[2]}:EEEEEEttttt33@.{c[1]} {r[7]}
|
|
{c[1]} ,.=::::it=.,{c[0]} `{c[1]} {c[2]}@EEEEEEtttz33QF{c[1]} {r[8]}
|
|
{c[1]} ;::::::::zt33){c[1]} {c[2]}'4EEEtttji3P*{c[1]} {r[9]}
|
|
{c[1]} :t::::::::tt33.{c[3]}:Z3z..{c[2]} `` {c[3]},..g.{c[1]} {r[10]}
|
|
{c[1]} i::::::::zt33F{c[1]} {c[3]}AEEEtttt::::ztF{c[1]} {r[11]}
|
|
{c[1]} ;:::::::::t33V{c[1]} {c[3]};EEEttttt::::t3{c[1]} {r[12]}
|
|
{c[1]} E::::::::zt33L{c[1]} {c[3]}@EEEtttt::::z3F{c[1]} {r[13]}
|
|
{c[1]} {{3=*^```'*4E3){c[1]} {c[3]};EEEtttt:::::tZ`{c[1]} {r[14]}
|
|
{c[1]} `{c[1]} {c[3]}:EEEEtttt::::z7{c[1]} {r[15]}
|
|
{c[1]} {c[3]}'VEzjt:;;z>*`{c[1]} {r[16]}
|
|
{c[1]} {r[17]}\n"""
|
|
}
|
|
|
|
|
|
# ----------- Configuration ----------- #
|
|
|
|
class Configuration(object):
|
|
def __init__(self):
|
|
"""
|
|
Represents the default configuration which will be used by Archey.
|
|
Values present in the dictionary below are needed.
|
|
New optional values may be added with `_updateRecursive()` method.
|
|
"""
|
|
self.config = {
|
|
'colors_palette': {
|
|
'use_unicode': False
|
|
},
|
|
'default_strings': {
|
|
'no_address': 'No Address',
|
|
'not_detected': 'Not detected',
|
|
'virtual_environment': 'Virtual Environment',
|
|
'bare_metal_environment': 'Bare-metal Environment'
|
|
},
|
|
'ip_settings': {
|
|
'lan_ip_max_count': 2,
|
|
'wan_ip_v6_support': True
|
|
},
|
|
'temperature': {
|
|
'char_before_unit': ' ',
|
|
'use_fahrenheit': False
|
|
},
|
|
'timeout': {
|
|
'ipv4_detection': 1,
|
|
'ipv6_detection': 1
|
|
}
|
|
}
|
|
|
|
# Let's "save" `STDERR` file descriptor for `suppress_warnings` option
|
|
self._stderr = sys.stderr
|
|
|
|
# Now, let's load each optional configuration file in a "regular" order
|
|
self.loadConfiguration('/etc/archey4/')
|
|
self.loadConfiguration(os.path.expanduser('~') + '/.config/archey4/')
|
|
self.loadConfiguration(os.path.dirname(os.path.realpath(__file__)))
|
|
|
|
def get(self, key, default=None):
|
|
"""
|
|
A binding method to imitate the `dict.get()` behavior.
|
|
"""
|
|
return self.config.get(key, default)
|
|
|
|
def loadConfiguration(self, path):
|
|
# If a previous configuration file has denied overriding...
|
|
if not self.config.get('allow_overriding', True):
|
|
# ... don't load this one.
|
|
return
|
|
|
|
if not path.endswith('/'):
|
|
path += '/'
|
|
|
|
path += 'config.json'
|
|
|
|
try:
|
|
with open(path) as file:
|
|
self._updateRecursive(self.config, json.load(file))
|
|
|
|
# If the user does not want any warning to appear : 2> /dev/null
|
|
if self.config.get('suppress_warnings', False):
|
|
# One more if statement to avoid multiple `open` calls.
|
|
if sys.stderr == self._stderr:
|
|
sys.stderr = open(os.devnull, 'w')
|
|
|
|
else:
|
|
# One more if statement to avoid useless assignments and...
|
|
# ... for closing previously opened new file descriptor.
|
|
if sys.stderr != self._stderr:
|
|
sys.stderr.close()
|
|
sys.stderr = self._stderr
|
|
|
|
except FileNotFoundError:
|
|
pass
|
|
|
|
# For backward compatibility with Python versions prior to 3.5.0
|
|
# we use `ValueError` instead of `json.JSONDecodeError`.
|
|
except ValueError as e:
|
|
print('Warning: {0} ({1})'.format(e, path), file=sys.stderr)
|
|
|
|
def _updateRecursive(self, oldDict, newDict):
|
|
"""
|
|
A method for recursively merging dictionaries as...
|
|
... `dict.update()` is not able to do this.
|
|
Original snippet taken from here :
|
|
https://gist.github.com/angstwad/bf22d1822c38a92ec0a9
|
|
"""
|
|
for key, value in newDict.items():
|
|
if key in oldDict and isinstance(oldDict[key], dict) \
|
|
and isinstance(value, dict):
|
|
self._updateRecursive(oldDict[key], value)
|
|
|
|
else:
|
|
oldDict[key] = value
|
|
|
|
|
|
# ---------- Global variables --------- #
|
|
|
|
# We create a global instance of our `Configuration` Class
|
|
config = Configuration()
|
|
|
|
# We'll list the running processes only one time
|
|
try:
|
|
PROCESSES = check_output(
|
|
['ps', '-u' + str(getuid()) if getuid() != 0 else '-ax',
|
|
'-o', 'comm', '--no-headers'], universal_newlines=True
|
|
).splitlines()
|
|
|
|
except FileNotFoundError:
|
|
print('Please, install first `procps` on your distribution.',
|
|
file=sys.stderr)
|
|
exit()
|
|
|
|
|
|
# ----------- Output handler ---------- #
|
|
|
|
class Output(object):
|
|
def __init__(self):
|
|
try:
|
|
lsbOutput = check_output(
|
|
['lsb_release', '-i', '-s'],
|
|
universal_newlines=True
|
|
).rstrip()
|
|
|
|
except FileNotFoundError:
|
|
print('Please, install first `lsb-release` on your distribution.',
|
|
file=sys.stderr)
|
|
exit()
|
|
|
|
if re.search(
|
|
'Microsoft',
|
|
check_output(['uname', '-r'], universal_newlines=True)):
|
|
self.distribution = Distributions.WINDOWS
|
|
|
|
else:
|
|
for distribution in Distributions:
|
|
if re.fullmatch(distribution.value, lsbOutput):
|
|
self.distribution = distribution
|
|
break
|
|
|
|
else:
|
|
self.distribution = Distributions.LINUX
|
|
|
|
# Each class output will be added in the list below afterwards
|
|
self.results = []
|
|
|
|
def append(self, key, value):
|
|
self.results.append(
|
|
'{0}{1}:{2} {3}'.format(
|
|
colorDict[self.distribution][1], key, colorDict['clear'], value
|
|
)
|
|
)
|
|
|
|
def output(self):
|
|
# Let's center the entries according to the logo (handles odd numbers)
|
|
self.results[0:0] = [''] * ((18 - len(self.results)) // 2)
|
|
self.results.extend([''] * (18 - len(self.results)))
|
|
|
|
print(
|
|
logosDict[self.distribution].format(
|
|
c=colorDict[self.distribution],
|
|
r=self.results
|
|
) + colorDict['clear']
|
|
)
|
|
|
|
|
|
# -------------- Entries -------------- #
|
|
|
|
class User:
|
|
def __init__(self):
|
|
self.value = getenv(
|
|
'USER',
|
|
config.get('default_strings')['not_detected']
|
|
)
|
|
|
|
|
|
class Hostname:
|
|
def __init__(self):
|
|
self.value = check_output(
|
|
['uname', '-n'],
|
|
universal_newlines=True
|
|
).rstrip()
|
|
|
|
|
|
class Model:
|
|
def __init__(self):
|
|
try:
|
|
with open('/sys/devices/virtual/dmi/id/product_name') as file:
|
|
model = file.read().rstrip()
|
|
|
|
except FileNotFoundError:
|
|
# The file above does not exist, is this device a Raspberry Pi ?
|
|
# Let's retrieve the Hardware and Revision IDs with `/proc/cpuinfo`
|
|
with open('/proc/cpuinfo') as file:
|
|
output = file.read()
|
|
|
|
hardware = re.search('(?<=Hardware\t: ).*', output)
|
|
revision = re.search('(?<=Revision\t: ).*', output)
|
|
|
|
# If the output contains 'Hardware' and 'Revision'...
|
|
if hardware and revision:
|
|
# ... let's set a pretty info string with these data
|
|
model = 'Raspberry Pi {0} (Rev. {1})'.format(
|
|
hardware.group(0),
|
|
revision.group(0)
|
|
)
|
|
|
|
else:
|
|
# A tricky way to retrieve some details about hypervisor...
|
|
# ... within virtual contexts.
|
|
# `archey` needs to be run as root although.
|
|
try:
|
|
virtWhat = ', '.join(
|
|
check_output(
|
|
['virt-what'],
|
|
stderr=DEVNULL, universal_newlines=True
|
|
).splitlines()
|
|
)
|
|
|
|
if virtWhat:
|
|
try:
|
|
# Sometimes we may gather info added by...
|
|
# ... hosting service provider this way
|
|
model = check_output(
|
|
['dmidecode', '-s', 'system-product-name'],
|
|
stderr=DEVNULL, universal_newlines=True
|
|
).rstrip()
|
|
|
|
except (FileNotFoundError, CalledProcessError):
|
|
model = config.get(
|
|
'default_strings'
|
|
)['virtual_environment']
|
|
|
|
model += ' ({0})'.format(virtWhat)
|
|
|
|
else:
|
|
model = config.get(
|
|
'default_strings'
|
|
)['bare_metal_environment']
|
|
|
|
except (FileNotFoundError, CalledProcessError):
|
|
model = config.get('default_strings')['not_detected']
|
|
|
|
self.value = model
|
|
|
|
|
|
class Distro:
|
|
def __init__(self):
|
|
self.value = '{0} {1}'.format(
|
|
check_output(
|
|
['lsb_release', '-d', '-s'],
|
|
universal_newlines=True
|
|
).rstrip(),
|
|
check_output(
|
|
['uname', '-m'],
|
|
universal_newlines=True
|
|
).rstrip()
|
|
)
|
|
|
|
|
|
class Kernel:
|
|
def __init__(self):
|
|
self.value = check_output(
|
|
['uname', '-r'],
|
|
universal_newlines=True
|
|
).rstrip()
|
|
|
|
|
|
class Uptime:
|
|
def __init__(self):
|
|
with open('/proc/uptime') as file:
|
|
fuptime = int(file.read().split('.')[0])
|
|
|
|
day, fuptime = divmod(fuptime, 86400)
|
|
hour, fuptime = divmod(fuptime, 3600)
|
|
minute = fuptime // 60
|
|
|
|
uptime = ''
|
|
uptime += ((str(day) + ' day' + ('s' if day > 1 else '') + ('' if minute == 0 and hour == 0 else (' and ' if (minute != 0 and hour == 0) or (minute == 0 and hour != 0) else ', '))) if day >= 1 else '')
|
|
uptime += ((str(hour) + ' hour' + ('s' if hour > 1 else '') + (' and ' if minute != 0 else '')) if hour >= 1 else '')
|
|
uptime += ((str(minute) + ' minute' + ('s' if minute > 1 else '')) if minute >= 1 else ('< 1 minute' if day == 0 and hour == 0 else ''))
|
|
|
|
self.value = uptime
|
|
|
|
|
|
class WindowManager:
|
|
def __init__(self):
|
|
try:
|
|
wm = re.search(
|
|
'(?<=Name: ).*',
|
|
check_output(
|
|
['wmctrl', '-m'],
|
|
stderr=DEVNULL, universal_newlines=True
|
|
)
|
|
).group(0)
|
|
|
|
except (FileNotFoundError, CalledProcessError):
|
|
for key in wmDict.keys():
|
|
if key in PROCESSES:
|
|
wm = wmDict[key]
|
|
break
|
|
|
|
else:
|
|
wm = config.get('default_strings')['not_detected']
|
|
|
|
self.value = wm
|
|
|
|
|
|
class DesktopEnvironment:
|
|
def __init__(self):
|
|
for key in deDict.keys():
|
|
if key in PROCESSES:
|
|
de = deDict[key]
|
|
break
|
|
|
|
else:
|
|
# Let's rely on an environment var if the loop above didn't `break`
|
|
de = getenv(
|
|
'XDG_CURRENT_DESKTOP',
|
|
config.get('default_strings')['not_detected']
|
|
)
|
|
|
|
self.value = de
|
|
|
|
|
|
class Shell:
|
|
def __init__(self):
|
|
self.value = getenv(
|
|
'SHELL',
|
|
config.get('default_strings')['not_detected']
|
|
)
|
|
|
|
|
|
class Terminal:
|
|
def __init__(self):
|
|
terminal = getenv(
|
|
'TERM',
|
|
config.get('default_strings')['not_detected']
|
|
)
|
|
|
|
# On systems with non-Unicode locales, we imitate '\u2588' character
|
|
# ... with '#' to display the terminal colors palette.
|
|
# This is the default option for backward compatibility.
|
|
colors = ' '.join([
|
|
'\x1b[0;3{0}m{1}\x1b[1;3{0}m{1}{2}'.format(
|
|
i,
|
|
'\u2588' if config.get('colors_palette')['use_unicode']
|
|
else '#',
|
|
colorDict['clear']
|
|
) for i in range(7, 0, -1)
|
|
])
|
|
|
|
self.value = '{0} {1}'.format(terminal, colors)
|
|
|
|
|
|
class Temperature:
|
|
def __init__(self):
|
|
temps = []
|
|
|
|
try:
|
|
# Let's try to retrieve a value from the Broadcom chip on Raspberry
|
|
temp = float(
|
|
re.search(
|
|
'\d+\.\d+',
|
|
check_output(
|
|
['/opt/vc/bin/vcgencmd', 'measure_temp'],
|
|
stderr=DEVNULL, universal_newlines=True
|
|
)
|
|
).group(0)
|
|
)
|
|
|
|
temps.append(
|
|
self._convertToFahrenheit(temp)
|
|
if config.get('temperature')['use_fahrenheit'] else temp
|
|
)
|
|
|
|
except (FileNotFoundError, CalledProcessError):
|
|
pass
|
|
|
|
# Now we just check for values within files present in the path below
|
|
for thermalFile in glob('/sys/class/thermal/thermal_zone*/temp'):
|
|
with open(thermalFile) as file:
|
|
temp = float(file.read().strip()) / 1000
|
|
if temp != 0.0:
|
|
temps.append(
|
|
self._convertToFahrenheit(temp)
|
|
if config.get('temperature')['use_fahrenheit']
|
|
else temp
|
|
)
|
|
|
|
if temps:
|
|
self.value = '{0}{2}{3} (Max. {1}{2}{3})'.format(
|
|
str(round(sum(temps) / len(temps), 1)),
|
|
str(round(max(temps), 1)),
|
|
config.get('temperature')['char_before_unit'],
|
|
'F' if config.get('temperature')['use_fahrenheit'] else 'C'
|
|
)
|
|
|
|
else:
|
|
self.value = config.get('default_strings')['not_detected']
|
|
|
|
"""
|
|
Simple Celsius to Fahrenheit conversion method
|
|
"""
|
|
def _convertToFahrenheit(self, temp):
|
|
return temp * (9 / 5) + 32
|
|
|
|
|
|
class Packages:
|
|
def __init__(self):
|
|
for packagesTool in [['dnf', 'list', 'installed'],
|
|
['dpkg', '--get-selections'],
|
|
['emerge', '-ep', 'world'],
|
|
['pacman', '-Q'],
|
|
['rpm', '-qa'],
|
|
['yum', 'list', 'installed'],
|
|
['zypper', 'search', '-i']]:
|
|
try:
|
|
results = check_output(
|
|
packagesTool,
|
|
stderr=DEVNULL, env={'LANG': 'C'}, universal_newlines=True
|
|
)
|
|
packages = results.count('\n')
|
|
|
|
if 'dnf' in packagesTool: # Deduct extra heading line
|
|
packages -= 1
|
|
|
|
elif 'dpkg' in packagesTool: # Packages removed but not purged
|
|
packages -= results.count('deinstall')
|
|
|
|
elif 'emerge' in packagesTool: # Deduct extra heading lines
|
|
packages -= 5
|
|
|
|
elif 'yum' in packagesTool: # Deduct extra heading lines
|
|
packages -= 2
|
|
|
|
elif 'zypper' in packagesTool: # Deduct extra heading lines
|
|
packages -= 5
|
|
|
|
break
|
|
|
|
except (FileNotFoundError, CalledProcessError):
|
|
pass
|
|
|
|
else:
|
|
packages = config.get('default_strings')['not_detected']
|
|
|
|
self.value = packages
|
|
|
|
|
|
class CPU:
|
|
def __init__(self):
|
|
with open('/proc/cpuinfo') as file:
|
|
self.value = re.sub(
|
|
'\s+', ' ',
|
|
re.search('(?<=model name\t: ).*', file.read()).group(0)
|
|
)
|
|
|
|
|
|
class GPU:
|
|
def __init__(self):
|
|
"""
|
|
Some explanations are needed here :
|
|
* We call `lspci` program to retrieve hardware devices
|
|
* We keep only the entries with "3D", "VGA" or "Display"
|
|
* We sort them in the same order as above (for relevancy)
|
|
"""
|
|
try:
|
|
lspci_output = sorted([
|
|
(i.split(': ')[0].split(' ')[1], i.split(': ')[1])
|
|
for i in check_output(
|
|
['lspci'], universal_newlines=True
|
|
).splitlines()
|
|
if '3D' in i or 'VGA' in i or 'Display' in i
|
|
], key=lambda x: len(x[1])
|
|
)
|
|
|
|
if lspci_output:
|
|
gpuinfo = lspci_output[0][1]
|
|
|
|
# If the line got too long, let's truncate it and add some dots
|
|
if len(gpuinfo) > 48:
|
|
# This call truncates `gpuinfo` with words preservation
|
|
gpuinfo = re.search(
|
|
'.{1,45}(?:\W|$)', gpuinfo
|
|
).group(0).strip() + '...'
|
|
|
|
else:
|
|
gpuinfo = config.get('default_strings')['not_detected']
|
|
|
|
except (FileNotFoundError, CalledProcessError):
|
|
gpuinfo = config.get('default_strings')['not_detected']
|
|
|
|
self.value = gpuinfo
|
|
|
|
|
|
class RAM:
|
|
def __init__(self):
|
|
try:
|
|
ram = ''.join(
|
|
filter(
|
|
re.compile('Mem').search,
|
|
check_output(
|
|
['free', '-m'],
|
|
env={'LANG': 'C'}, universal_newlines=True
|
|
).splitlines()
|
|
)
|
|
).split()
|
|
used = float(ram[2])
|
|
total = float(ram[1])
|
|
|
|
except (IndexError, FileNotFoundError):
|
|
# An in-digest one-liner to retrieve memory info into a dictionary
|
|
with open('/proc/meminfo') as file:
|
|
ram = {
|
|
i.split(':')[0]: float(i.split(':')[1].strip(' kB')) / 1024
|
|
for i in filter(None, file.read().splitlines())
|
|
}
|
|
|
|
total = ram['MemTotal']
|
|
# Here, let's imitate the `free` command behavior
|
|
# (https://gitlab.com/procps-ng/procps/blob/master/proc/sysinfo.c#L787)
|
|
used = total - (ram['MemFree'] + ram['Cached'] + ram['Buffers'])
|
|
if used < 0:
|
|
used += ram['Cached'] + ram['Buffers']
|
|
|
|
self.value = '{0}{1} MB{2} / {3} MB'.format(
|
|
colorDict['sensors'][int(((used / total) * 100) // 33.34)],
|
|
int(used),
|
|
colorDict['clear'],
|
|
int(total)
|
|
)
|
|
|
|
|
|
class Disk:
|
|
def __init__(self):
|
|
total = re.sub(
|
|
',', '.',
|
|
check_output([
|
|
'df', '-Tlh', '-B', 'GB', '--total',
|
|
'-t', 'ext4', '-t', 'ext3', '-t', 'ext2',
|
|
'-t', 'reiserfs', '-t', 'jfs', '-t', 'zfs',
|
|
'-t', 'ntfs', '-t', 'fat32', '-t', 'btrfs',
|
|
'-t', 'fuseblk', '-t', 'xfs',
|
|
'-t', 'simfs', '-t', 'tmpfs'
|
|
], universal_newlines=True
|
|
).splitlines()[-1]
|
|
).split()
|
|
|
|
self.value = '{0}{1}{2} / {3}'.format(
|
|
colorDict['sensors'][int(float(total[5][:-1]) // 33.34)],
|
|
re.sub('GB', ' GB', total[3]), colorDict['clear'],
|
|
re.sub('GB', ' GB', total[2])
|
|
)
|
|
|
|
|
|
class LAN_IP:
|
|
def __init__(self):
|
|
try:
|
|
addresses = check_output(
|
|
['hostname', '-I'],
|
|
stderr=DEVNULL, universal_newlines=True
|
|
).split()
|
|
|
|
except (CalledProcessError, FileNotFoundError):
|
|
# Slow manual workaround for old `inetutils` versions, with `ip`
|
|
addresses = check_output(
|
|
['cut', '-d', ' ', '-f', '4'],
|
|
universal_newlines=True,
|
|
stdin=Popen(['cut', '-d', '/', '-f', '1'],
|
|
stdout=PIPE,
|
|
stdin=Popen(['tr', '-s', ' '],
|
|
stdout=PIPE,
|
|
stdin=Popen(['grep', '-E',
|
|
'scope (global|site)'
|
|
], stdout=PIPE,
|
|
stdin=Popen(['ip',
|
|
'-o',
|
|
'addr',
|
|
'show',
|
|
'up'],
|
|
stdout=PIPE
|
|
).stdout
|
|
).stdout
|
|
).stdout
|
|
).stdout
|
|
).splitlines()
|
|
|
|
# Use list slice to save only `lan_ip_max_count` from `addresses`.
|
|
# If set to `False`, don't modify the list.
|
|
# This option is still optional.
|
|
self.value = ', '.join(
|
|
addresses[:(
|
|
config.get('ip_settings')['lan_ip_max_count']
|
|
if config.get('ip_settings')['lan_ip_max_count'] is not False
|
|
else len(addresses)
|
|
)]
|
|
) or config.get('default_strings')['no_address']
|
|
|
|
|
|
class WAN_IP:
|
|
def __init__(self):
|
|
# IPv6 address retrieval (unless the user doesn't want it).
|
|
if config.get('ip_settings')['wan_ip_v6_support']:
|
|
try:
|
|
ipv6_value = check_output([
|
|
'dig', '+short', '-6', 'aaaa', 'myip.opendns.com',
|
|
'@resolver1.ipv6-sandbox.opendns.com'
|
|
], timeout=config.get('timeout')['ipv6_detection'],
|
|
stderr=DEVNULL, universal_newlines=True
|
|
).rstrip()
|
|
|
|
except (FileNotFoundError, TimeoutExpired, CalledProcessError):
|
|
try:
|
|
ipv6_value = check_output([
|
|
'wget', '-qO-', 'https://v6.ident.me/'
|
|
], timeout=config.get('timeout')['ipv6_detection'],
|
|
universal_newlines=True
|
|
)
|
|
|
|
except (CalledProcessError, TimeoutExpired):
|
|
# It looks like this user doesn't have any IPv6 address...
|
|
# ... or is not connected to Internet.
|
|
ipv6_value = None
|
|
|
|
except FileNotFoundError:
|
|
ipv6_value = None
|
|
print('Warning: `wget` has not been found on your system.',
|
|
file=sys.stderr)
|
|
|
|
else:
|
|
ipv6_value = None
|
|
|
|
# IPv4 addresses retrieval (anyway).
|
|
try:
|
|
ipv4_value = check_output([
|
|
'dig', '+short', 'myip.opendns.com', '@resolver1.opendns.com'
|
|
], timeout=config.get('timeout')['ipv4_detection'],
|
|
stderr=DEVNULL, universal_newlines=True
|
|
).rstrip()
|
|
|
|
except (FileNotFoundError, TimeoutExpired, CalledProcessError):
|
|
try:
|
|
ipv4_value = check_output([
|
|
'wget', '-qO-', 'https://v4.ident.me/'
|
|
], timeout=config.get('timeout')['ipv4_detection'],
|
|
universal_newlines=True
|
|
)
|
|
|
|
except (CalledProcessError, TimeoutExpired):
|
|
# This user looks not connected to Internet...
|
|
ipv4_value = None
|
|
|
|
except FileNotFoundError:
|
|
ipv4_value = None
|
|
# If statement so as to not print the same message twice.
|
|
if not config.get('ip_settings')['wan_ip_v6_support']:
|
|
print('Warning: `wget` has not been found on your system.',
|
|
file=sys.stderr)
|
|
|
|
self.value = ', '.join(
|
|
filter(None, (ipv4_value, ipv6_value))
|
|
) or config.get('default_strings')['no_address']
|
|
|
|
|
|
# ----------- Classes Index ----------- #
|
|
|
|
class Classes(Enum):
|
|
User = User
|
|
Hostname = Hostname
|
|
Model = Model
|
|
Distro = Distro
|
|
Kernel = Kernel
|
|
Uptime = Uptime
|
|
WindowManager = WindowManager
|
|
DesktopEnvironment = DesktopEnvironment
|
|
Shell = Shell
|
|
Terminal = Terminal
|
|
Packages = Packages
|
|
Temperature = Temperature
|
|
CPU = CPU
|
|
GPU = GPU
|
|
RAM = RAM
|
|
Disk = Disk
|
|
LAN_IP = LAN_IP
|
|
WAN_IP = WAN_IP
|
|
|
|
|
|
# ---------------- Main --------------- #
|
|
|
|
def main():
|
|
output = Output()
|
|
for key in Classes:
|
|
if config.get('entries', {}).get(key.name, True):
|
|
output.append(key.name, key.value().value)
|
|
|
|
output.output()
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|