Имена файлов удаляются с помощью системных вызовов 
ㅤ
Ну дак вот. Когда счетчик ссылок (имен) становится равен нулю — файл удаляется.
Физическое удаление (блоки маркируются свободными) происходит когда больше никто не удерживает файл. То есть процесс закрыл последний дескриптор ассоциированный с файлом.
Давай попробуем поймать (открыть файл) до того, как запустится вызов
Создаем искусственный временный файл:
Создаем и запускаем скрипт:
Что тут происходит?
Итоги:
Эта фишка демонстрирует, что можно удалить файл, но продолжить его использовать если он был открыт на момент удаления.
Это классическая Unix-модель: имя файла — не сам файл, а просто ссылка.
А тут как-то рассказывал, как восстанавливать удаленные файлы, в том числе и запущенные.
Чтиво почитать:
man 2 unlink
man 2 unlinkat
🛠  #linux #bash
—
✅  @bashdays / @linuxfactory / @blog
unlink, unlinkat.ㅤ
Ну дак вот. Когда счетчик ссылок (имен) становится равен нулю — файл удаляется.
Физическое удаление (блоки маркируются свободными) происходит когда больше никто не удерживает файл. То есть процесс закрыл последний дескриптор ассоциированный с файлом.
Давай попробуем поймать (открыть файл) до того, как запустится вызов
unlink.Создаем искусственный временный файл:
printf '%s\n' {a..z} > /tmp/sh-thd-bashdaysСоздаем и запускаем скрипт:
#!/bin/bash
file=/tmp/sh-thd-bashdays
exec 3< "$file"
rm -vf "$file" # Имени уже нет
read -N 4096 -ru 3 # Но мы читаем
printf '%s' $REPLY # Выводим
exec 3>&-
exit
Что тут происходит?
exec 3< - файл открывается на чтение, привязывается к дескриптору 3 и с этого момента файл начинает жить в «памяти» в open file table ядра, даже если мы его ёбнем удалим. Дескриптор 3 теперь указывает на открытый файл, независимо от имени.rm -vf - файл удаляется из файловой системы, имени больше нет, НО, его содержимое еще доступно, потому что он открыт.read -N 4096 - читаем до 4096 байт из открытого файла через дескриптор 3.exec 3>& - закрываем дескриптор 3 и после этого файл окончательно исчезнет, если его больше никто не держит, а ядро высвобождает ресурсы.Итоги:
Эта фишка демонстрирует, что можно удалить файл, но продолжить его использовать если он был открыт на момент удаления.
Это классическая Unix-модель: имя файла — не сам файл, а просто ссылка.
Если держишь дескриптор — файл всё ещё существует.
А тут как-то рассказывал, как восстанавливать удаленные файлы, в том числе и запущенные.
Чтиво почитать:
man 2 unlink
man 2 unlinkat
—
Please open Telegram to view this post
    VIEW IN TELEGRAM
  CRON в первый рабочий день месяца
ㅤ
Да, ключевой момент тут именно в «рабочий день».
Задачка вроде не тривиальная, но как оказалось
Мы с тобой не пальцем деланы, изобретем свой велосипед!
Чтобы реализовать желаемое, я изобрел такую кишку:
Выглядит монструозно, но блядь... Можно в сам скрипт проверку забить, но это еще один костыль.
Что тут происходит?
Короче…
То есть АПИха возвращает
Тут еще бы для
Всегда старайся указывать полные пути до программ и утилит. Это распространенный кейс, крон вроде отрабатывает, но ничего не работает. А оно просто не знает откуда запускать, то что ты ему прописал.
Если знаешь еще какие-то способы, велком в комментарии.
UPD: более корректные алгоритмы решения этой задачи смотрим в комментах, всем спасибо!
🛠  #bash #linux
—
✅  @bashdays ✅  @linuxfactory ✅  @blog
ㅤ
Да, ключевой момент тут именно в «рабочий день».
Задачка вроде не тривиальная, но как оказалось
cron нихуя не умеет определять рабочий сегодня день или нет. И systemd timer тоже не умеет. Никто ничо блядь не умеет.Мы с тобой не пальцем деланы, изобретем свой велосипед!
Чтобы реализовать желаемое, я изобрел такую кишку:
0 8 1-3 * * [ "$(date +\%u)" -lt 6 ] && [ "$(curl -s https://isdayoff.ru/$(date +\%Y\%m\%d))" = "0" ] && /usr/local/sbin/bashdays.sh
Выглядит монструозно, но блядь... Можно в сам скрипт проверку забить, но это еще один костыль.
Что тут происходит?
Короче…
0 8 1-3 * * — Запуск 1–3 числа месяца в 08:00. Потому что первый рабочий день месяца может выпасть на 1-е число, если это будний день. 2-е число, если 1-е — выходной. 3-е число, если 1-е и 2-е — выходные (например, сб + вс).[ "$(date +%u)" -lt 6 ] — Проверяем, что сегодня будний день (1–5 = Пн–Пт). Пусть тебя здесь 6ка не смущает. Она означает: день меньше 6, т.е. 1, 2, 3, 4, 5 — это будни (Пн – Пт).curl ... = "0" — Проверка, что сегодня рабочий день по календарю РФ, выполняется через АПИху производственного календаря.&& /opt/scripts/first-workday.sh — Выполняем только если сошлись оба условия.То есть АПИха возвращает
0 если сегодня будни. Если выходной, то оно вернет что-то другое.Тут еще бы для
curl и date указать что-то вроде CURL=$(which curl), а то крон частенько залупается и не знает откуда запускать эти команды. Всегда старайся указывать полные пути до программ и утилит. Это распространенный кейс, крон вроде отрабатывает, но ничего не работает. А оно просто не знает откуда запускать, то что ты ему прописал.
Да, есть зависимость от внешнего API и порой это пиздец хуёва. Отвалилась АПИха, скрипт не получил данные. Поэтому рекомендую сделать себе собственный микросервис с АПИхой и ни от кого не зависеть. Тем более если сервер в оффлайне, запрос на внешнюю АПИху он сделать не сможет.
Если знаешь еще какие-то способы, велком в комментарии.
UPD: более корректные алгоритмы решения этой задачи смотрим в комментах, всем спасибо!
—
Please open Telegram to view this post
    VIEW IN TELEGRAM
  6 60
  Слыхал про 
Если коротко, это штуки, которые не дают запустить второй экземпляр скрипта/программы, если первый ещё работает.
ㅤ
Всё это и многое другое входит в пакет
Например (нажми на спойлер):
Вернемся к
Допустим у тебя что-то запустилось по крону, не успело завершиться и через промежуток снова запускается. Наплодилось 100500 процессов, сервак встал раком, а потом и тебя поставили раком.
Давай на практике!
В первом терминале запускаем:
Во втором терминале запускаем:
И получаем:
По умолчанию
Теперь когда файл
Как lckdo работает с lock-файлом.
1. Файл
2. Блокировку держит процесс, который открыл файл и вызвал
3. Когда процесс завершается — блокировка автоматически снимается.
Даже если файл
Всё логично, вот еще одни пример с кроном:
Скрипт не будет запущен, если предыдущий запуск не завершился.
Такие дела. Бери на вооружение, возможно где-то в хозяйстве сгодится.
🛠  #linux
—
✅  @bashdays ✅  @linuxfactory ✅  @blog
with-lock-ex, flock, lckdo?Если коротко, это штуки, которые не дают запустить второй экземпляр скрипта/программы, если первый ещё работает.
ㅤ
Всё это и многое другое входит в пакет
moreutils, в убунтах из коробки я их не нашел, пришлось ставить. Но это детали. В этот пакет входит еще дюжина полезных хуёвин.Например (нажми на спойлер):
sudo apt install moreutils
chronic — показывает вывод команды, только если она завершилась с ошибкой.
combine — логические операции над двумя файлами (как `sort` + `comm`, но проще).
errno — выводит описание кода ошибки (EACCES 13 Permission denied).
ifdata — dыводит информацию о сетевых интерфейсах в скриптабельном виде.
ifne — выполняет команду только если предыдущая команда что-то вывела (if-not-empty).
isutf8 — проверяет, является ли файл валидным UTF-8.
и так далее…
Вернемся к
lckdo (Lock and Do). Это обёртка вокруг flock. Как и написал выше, оно не позволяет запуститься второму экземпляру программы/скрипты.Допустим у тебя что-то запустилось по крону, не успело завершиться и через промежуток снова запускается. Наплодилось 100500 процессов, сервак встал раком, а потом и тебя поставили раком.
lckdo — cтавит блокировку на файл, если блокировка прошла — выполняет указанную команду, если не удалось — завершает выполнение (по умолчанию).Давай на практике!
В первом терминале запускаем:
lckdo /tmp/bashdays.lock bash -c 'echo start; sleep 60; echo done'
Во втором терминале запускаем:
lckdo /tmp/bashdays.lock echo "Hello bashdays"
И получаем:
lckdo: lockfile /tmp/bashdays.lock' is already lockedПо умолчанию
lckdo ничего не ждет, если нужно, чтобы он ждал до освобождения блокировки, добавляем -w.lckdo -w /tmp/bashdays.lock echo "I'm free"
Теперь когда файл
/tmp/bashdays.lock будет отпущен, отработает команда и выведет на экран I'm free.Как lckdo работает с lock-файлом.
1. Файл
/tmp/bashdays.lock сам по себе ничего не блокирует.2. Блокировку держит процесс, который открыл файл и вызвал
flock/lckdo на него.3. Когда процесс завершается — блокировка автоматически снимается.
Даже если файл
/tmp/bashdays.lock остаётся на диске — он больше не заблокирован, и lckdo снова сможет его использовать.Всё логично, вот еще одни пример с кроном:
* * * * * lckdo /tmp/bashdays.lock /usr/local/bin/bashdays.sh
Скрипт не будет запущен, если предыдущий запуск не завершился.
Такие дела. Бери на вооружение, возможно где-то в хозяйстве сгодится.
—
Please open Telegram to view this post
    VIEW IN TELEGRAM
  Сопроцессы. Практика. Часть Вторая.
🔤 🔤 🔥 🔤 🔤 🔤 🔤 
coproc хорошо подходит для общения в клиент-серверном режиме. Для примера попробуем подключиться к POP3 серверу с шифрованием ssl прямо из bash-скрипта.
ㅤ
Сам ssl несколько сложноват для bash, поэтому в качестве посредника будем использовать openssl s_client.
Протокол и команды POP3 лучше посмотреть на википедии.
1. Cоздим сопроцесс. Для этого запустим openssl в режиме s_client. При этом из дескриптора POP3_CONN[0] можно читать данные от сопроцесса.
В дескриптор POP3_CONN[1] можно писать для сопроцесса.
При записи используем перенаправление >&${POP3_CONN[1] . При чтении тоже можно использовать перенаправление, но поскольку у команды read есть ключ -u красивее воспользоваться им.
2. Аутентифицируемся
3. Закроем сессию и дескрипторы.
Задержки 0.3 секунды при отправке нужны для того, чтобы сервер успел сформировать ответ.
Ошибки -ERR не обрабатывал. В случае чего команда read завершится по таймауту в 2 сек. (-t 2)
${REC//$'\r'/} конструкция удаляет cr, потому что POP3 сервер отвечает c lfcr.
help coproc
help read
man openssl
вики POP3
🛠  #bash #linux
—
✅  @bashdays ✅  @linuxfactory ✅  @blog
coproc хорошо подходит для общения в клиент-серверном режиме. Для примера попробуем подключиться к POP3 серверу с шифрованием ssl прямо из bash-скрипта.
ㅤ
Сам ssl несколько сложноват для bash, поэтому в качестве посредника будем использовать openssl s_client.
Протокол и команды POP3 лучше посмотреть на википедии.
1. Cоздим сопроцесс. Для этого запустим openssl в режиме s_client. При этом из дескриптора POP3_CONN[0] можно читать данные от сопроцесса.
В дескриптор POP3_CONN[1] можно писать для сопроцесса.
При записи используем перенаправление >&${POP3_CONN[1] . При чтении тоже можно использовать перенаправление, но поскольку у команды read есть ключ -u красивее воспользоваться им.
2. Аутентифицируемся
3. Закроем сессию и дескрипторы.
# Функция для отправки команд серверу
function SEND_CMD() {
sleep 0.3
echo "$@" >&${POP3_CONN[1]}
sleep 0.3
}
# аутентификация. Обычный логин
function POP3_LOGIN() {
declare REC
declare -a AREC
# проверка соединения
read -ert 2 -u ${POP3_CONN[0]} REC
read -ra AREC <<<${REC//$'\r'/}
if [[ "${AREC[0]}" == "+OK" ]];then
# Отправляем логин
SEND_CMD "USER $USER"
read -ert 2 -u ${POP3_CONN[0]} REC
read -ra AREC <<<${REC//$'\r'/}
if [[ "${AREC[0]}" == "+OK" ]];then
# Отправляем пароль
SEND_CMD "PASS $PASS"
read -ert 2 -u "${POP3_CONN[0]}" REC
read -ra AREC <<<${REC//$'\r'/}
if [[ "${AREC[0]}" == "+OK" ]];then
return 0 # аутентификация успешна
else
return 3 # не правильный пароль
fi
else
return 2 #не правильный login
fi
else
return 1 # ошибка соединения с сервером
fi
}
#Выход и закрытие дескрипторов.
function POP3_QUIT(){
SEND_CMD "QUIT"
# Закрываем coproc
exec ${POP3_CONN[0]}<&-
exec ${POP3_CONN[1]}>&-
}
Задержки 0.3 секунды при отправке нужны для того, чтобы сервер успел сформировать ответ.
Ошибки -ERR не обрабатывал. В случае чего команда read завершится по таймауту в 2 сек. (-t 2)
${REC//$'\r'/} конструкция удаляет cr, потому что POP3 сервер отвечает c lfcr.
#!/bin/bash
SERVER="server"
PORT=995
USER="user@server"
PASS="StrongPass"
# создаем сопроцесс и соединяемся с сервером pop3
coproc POP3_CONN { openssl s_client -connect "${SERVER}:${PORT}" -quiet 2>/dev/null;}
POP3_LOGIN
POP3_QUIT
help coproc
help read
man openssl
вики POP3
—
Please open Telegram to view this post
    VIEW IN TELEGRAM
  2 21
  Сопроцессы. Практика. Часть Третья.
🔤 🔤 🔥 🔤 🔤 🔤 🔤 
Это уже больше не сопроцессы, а про то, как принять почту в скрипте bash.
ㅤ
Соединение с POP3 сервером есть. Аутентификация тоже. Осталось написать что-нибудь полезное.
Финальный код
help coproc
help read
man openssl
🛠  #bash #linux
—
✅  @bashdays ✅  @linuxfactory ✅  @blog
Это уже больше не сопроцессы, а про то, как принять почту в скрипте bash.
ㅤ
Соединение с POP3 сервером есть. Аутентификация тоже. Осталось написать что-нибудь полезное.
# возвращает число писем в ящике
function POP3_STAT(){
declare -a AREC
declare REC
SEND_CMD "STAT"
read -ert 2 -u ${POP3_CONN[0]} REC
read -ra AREC <<<${REC//$'\r'/}
if [[ ${AREC[0]} == "+OK" ]];then
echo ${AREC[1]} # число сообщений
return 0
else
echo 0
return 1
fi
}
#Помечает к удалению указанное письмо
function POP3_DELE(){
declare -i MSG_NUM=${1:-1} # по умолчанию первое
declare -a AREC
declare REC
SEND_CMD "DELE $MSG_NUM" #удаляем указанное сообщение
read -ert 2 -u ${POP3_CONN[0]} REC
read -ra AREC <<<${REC//$'\r'/}
if [[ ${AREC[0]} == "+OK" ]];then
return 0
else
return 1
fi
}
# читает письмо с заголовками
function POP3_RETR(){
declare -i MSG_NUM=${1:-1} # по умолчанию первое
declare -a AREC
declare REC
SEND_CMD "RETR $MSG_NUM" #читаем указанное сообщение
read -ert 2 -u ${POP3_CONN[0]} REC
read -ra AREC <<<${REC//$'\r'/}
if [[ ${AREC[0]} == "+OK" ]];then
while read -r -t 2 -u ${POP3_CONN[0]} REC ; do
REC=${REC//$'\r'/}
echo "$REC"
if [[ "$REC" == "." ]];then
return 0 # msg end
fi
done
else
return 1
fi
}
# читает указанное число строк письма
function POP3_TOP(){
declare -i MSG_NUM=${1:-1} # по умолчанию первое
declare -i STR_NUM=${2:-1} # по умолчанию одна строка
declare -a AREC
declare REC
#читаем указанное сообщение
SEND_CMD "TOP $MSG_NUM $STR_NUM"
read -ert 2 -u ${POP3_CONN[0]} REC
read -ra AREC <<<${REC//$'\r'/}
if [[ ${AREC[0]} == "+OK" ]];then
while read -ert 2 -u ${POP3_CONN[0]} REC ; do
REC=${REC//$'\r'/}
echo "$REC"
if [[ "$REC" == "." ]];then
return 0
fi
done
else
return 1
fi
}
Финальный код
#!/bin/bash
SERVER="server"
PORT=995
USER="user@server"
PASS="StrongPass"
coproc POP3_CONN { openssl s_client -connect "${SERVER}:${PORT}" -quiet 2>/dev/null;}
POP3_LOGIN && echo POP3_LOGIN OK
MSG_NUM=$(POP3_STAT)
#цикл перебора сообщений
while ((MSG_NUM));do
POP3_TOP $MSG_NUM 1 # Заголовки + 1 строку сообщения
# POP3_RETR $MSG_NUM # сообщения целиком
# POP3_DELE $MSG_NUM # помечаем к удалению.
((--MSG_NUM))
done
POP3_QUIT
help coproc
help read
man openssl
—
Please open Telegram to view this post
    VIEW IN TELEGRAM
  Хочешь устроить себе челлендж на знание командной строки? Да пожалуйста, лови тренажер по Linux-терминалу. Правда он на английском, но мы с тобой тоже не пальцем деланные. 
Я даж успешно прошел первую задачку, правда по-олдскульному и затронул все бед-практики, которые только существуют.
ㅤ
Установка простая:
Здесь я использовал
Что прикольно, если совсем тупой, то можно получить подсказку, будет показано несколько решений. Подсказка открывается по
Сможешь выбить все 77 вопросов без подсказок?
Исходники этого тренажера лежат тут, а видосик с работой можешь посмотреть тут.
🛠  #linux #bash
—
✅  @bashdays ✅  @linuxfactory ✅  @blog
Тренажер содержит 77 вопросов. Вполне достаточно чтобы заебаться.
Я даж успешно прошел первую задачку, правда по-олдскульному и затронул все бед-практики, которые только существуют.
ㅤ
Установка простая:
cd /tmp
python3 -m venv textual_apps
cd textual_apps
source bin/activate
pip install cliexercises
cliexercises
Здесь я использовал
tmp папку и venv, чтобы систему всяким гавно не засорять, а чисто поиграться.Что прикольно, если совсем тупой, то можно получить подсказку, будет показано несколько решений. Подсказка открывается по
CTRL+S.Сможешь выбить все 77 вопросов без подсказок?
Исходники этого тренажера лежат тут, а видосик с работой можешь посмотреть тут.
—
Please open Telegram to view this post
    VIEW IN TELEGRAM
  3 64
  Ремонтировал внешний диск, в моменте отвалился от малины и я получил RAW вместо ext3. Печалька. Бекапы естественно я никакие не делал, хотя 5 лет собирался этим заняться.
Велосипед я изобретать не стал, а так же не стал брать сразу какие-то продвинутые инструменты для восстановления. Решил воспользоваться нативным
ㅤ
Пошел погуглил синтаксис, не каждый же день этой хернёй пользуешься. А там блядь:
Хмм... помимо
TL:DR: НИЧЕМ!
Ща, расскажу. Короче в чистом виде
Автоопределение это заебись, но порой
Чтобы узнать, что запустит
В результате получишь:
А еще у
Пример:
Принудительная проверка + удаление "мусорных" блоков на SSD.
# Как fsck определяет тип файловой системы
Порядок определения:
1. Смотрит в /etc/fstab и выгребает третий столбец.
2. Если в
3. Если определить не получилось, пиздует в
Вот и вся наука. В кишки лезть уже не будет, этой информации вполне достаточно.
Ну и чтобы на каждую ошибку не вводить
Оно там само пошуршит, все исправит и будет тебе счастье. Изучай.
🛠  #linux
—
✅  @bashdays ✅  @linuxfactory ✅  @blog
Как говорится - пока гром не грянет мужик не перекрестится.
Велосипед я изобретать не стал, а так же не стал брать сразу какие-то продвинутые инструменты для восстановления. Решил воспользоваться нативным
fsck, чем чёрт не шутит.ㅤ
Пошел погуглил синтаксис, не каждый же день этой хернёй пользуешься. А там блядь:
fsck -y /dev/sdb
fsck.ext3 -y /dev/sdb
Хмм... помимо
fsck.ext3 есть еще и fsck.ext4 и еще несколько штук. Ёбтвою мать. Отправляемся ресерчить, чем эта поебота отличается от обычного fsck.TL:DR: НИЧЕМ!
Ща, расскажу. Короче в чистом виде
fsck это обёртка, универсальная оболочка, которая автоматом определяет тип файловой системы и затем уже запускает условно fsck.ext3, ну или какая там у тебя на дисках.Автоопределение это заебись, но порой
fsck не хочет ничего проверять. Поэтому самостоятельно определяем тип своей файловой системы и в зависимости от результата запускаем fsck.ext3 и т.п.Чтобы узнать, что запустит
fsck делаем так:fsck -N /dev/sda1
В результате получишь:
[/usr/sbin/fsck.ext4 (1) -- fsck.ext4 /dev/sda1
А еще у
fsck нет ключа - [-E extended-options]. В нее можно передать:-E discard - Включает TRIM (удаление неиспользуемых блоков на SSD) во время проверки. Аналог fstrim, но в процессе fsck.-E journal_only - Проверяет только журнал ext3/ext4, не сканируя всю ФС. Быстро, но полезно только в определённых сценариях.-E frag - Проводит анализ фрагментации. Полезно, если интересует дефрагментация ext4.-E bmap2extent - Преобразует старые "indirect" блоки в extent-формат (для старых ext4).-E test_fs - Включает особое поведение для тестирования (не используется в продакшене).Пример:
fsck.ext4 -f -E discard /dev/sda1
Принудительная проверка + удаление "мусорных" блоков на SSD.
# Как fsck определяет тип файловой системы
Порядок определения:
1. Смотрит в /etc/fstab и выгребает третий столбец.
2. Если в
fstab хуй, то оно запускает blkid /dev/sda13. Если определить не получилось, пиздует в
/etc/filesystems, но в большинстве случаев такого файла в современных дистрибутивах нет. Этот файл опционален.Вот и вся наука. В кишки лезть уже не будет, этой информации вполне достаточно.
Ну и чтобы на каждую ошибку не вводить
y, пропиши автофикс:fsck -y /dev/sda1
Оно там само пошуршит, все исправит и будет тебе счастье. Изучай.
—
Please open Telegram to view this post
    VIEW IN TELEGRAM
  4 63
  Забавная ситуёвина с ubuntu 24. После того,  как человек поменял в файле 
По-прежнему слушался порт 22. Хм... Хуйня какая-то.
ㅤ
Всё оказалось прозаичнее. В новых версиях дистрибутива, ssh демон стал использовать сокетовую модель для подключения клиентов. Как раз недавно мы разбирали
Ну так вот. Если подключиться к консоли дистрибутива (не по ssh), то обнаружим, что никакой ssh сервис не запущен. Однако...
Но если выполнить:
Да ёбтвою мать! Разбираемся.
Суть такая. Для ssh используется не
Пиздец конечно конструкция. Если посмотреть файл
Смотрим на
А чё блядь делать?
Снимать штаны и бегать. А если серьезно, то решается это просто. После правки конфига
Перезапускаем именно юнит сокета. После этого порт изменится. А в файле
Красота. Если тебя удручают эти нововведения, то можно вернуться к привычной схеме.
Делается это так:
Способ описан на официальном сайте бубунты.
Вот такие пироги.
🛠  #linux
—
✅  @bashdays ✅  @linuxfactory ✅  @blog
/etc/ssh/sshd_config порт с 22 на 2222 и сделал systemctl restart ssh - ничего не произошло.По-прежнему слушался порт 22. Хм... Хуйня какая-то.
ㅤ
Всё оказалось прозаичнее. В новых версиях дистрибутива, ssh демон стал использовать сокетовую модель для подключения клиентов. Как раз недавно мы разбирали
systemd и его функционал, поищи по тегу #systemdНу так вот. Если подключиться к консоли дистрибутива (не по ssh), то обнаружим, что никакой ssh сервис не запущен. Однако...
Но если выполнить:
ss -tln то увидим:tcp LISTEN 0 4096 *:22 *:*
Да ёбтвою мать! Разбираемся.
Суть такая. Для ssh используется не
ssh.service юнит, а ssh.socket. А как мы знаем юнит socket не держит процесс постоянно запущенным. Экономия ресурсов и т.п. А вот когда кто-то обращается к порту 22, запрос улетает на сокет systemd, который уже и запускает сервис ssh.service..Пиздец конечно конструкция. Если посмотреть файл
ssh.socket, в нём будет:[Unit]
Description=OpenBSD Secure Shell server socket
Before=sockets.target ssh.service
ConditionPathExists=!/etc/ssh/sshd_not_to_be_run
[Socket]
ListenStream=0.0.0.0:22
ListenStream=[::]:22
BindIPv6Only=ipv6-only
Accept=no
FreeBind=yes
[Install]
WantedBy=sockets.target
RequiredBy=ssh.service
Смотрим на
ListenStream, вот тебе и 22 порт.А чё блядь делать?
Снимать штаны и бегать. А если серьезно, то решается это просто. После правки конфига
/etc/ssh/sshd_config делаем:systemctl daemon-reload
systemctl restart ssh.socket
Перезапускаем именно юнит сокета. После этого порт изменится. А в файле
ssh.socket будет такое:[Socket]
ListenStream=0.0.0.0:2222
ListenStream=[::]:2222
Красота. Если тебя удручают эти нововведения, то можно вернуться к привычной схеме.
Делается это так:
systemctl disable --now ssh.socket
rm -f /etc/systemd/system/ssh.service.d/00-socket.conf
rm -f /etc/systemd/system/ssh.socket.d/addresses.conf
systemctl daemon-reload
systemctl enable --now ssh.service
Способ описан на официальном сайте бубунты.
Вот такие пироги.
Ну а чем отличается ssh_config от sshd_config я описывал в этом посте.
—
Please open Telegram to view this post
    VIEW IN TELEGRAM
  9 121
  Как-то давненько пришлось мне отлаживать большущую APIху, но под рукой была только консолька. Ни о каком GUI и речь не шла. А пулять запросы через 
ㅤ
Тем более хотелось что-то организованное и визуально приятное.
Postman сразу мимо, это гавно сделали настолько перегруженным, что им пользоваться уже нереально, да и под чистым терминалом его не запустишь.
Выбор пал на Posting. Это TUI клиент со всем необходимым функционалом.
Так он и прижился, сейчас если что-то нужно отладить или потестить у меня есть эта замечательная тулза. Не перегружена хуйнёй и дружелюбная. Есть все самое необходимое, даже мышкой можно тыкать. Ну и сессии сохраняет со всеми запросами.
Ставится элементарно:
Для хакерменов прям мастхев. Забирай в копилку, годнота!
🛠  #linux #utilites
—
✅  @bashdays ✅  @linuxfactory ✅  @blog
curl я заебался.ㅤ
Тем более хотелось что-то организованное и визуально приятное.
Postman сразу мимо, это гавно сделали настолько перегруженным, что им пользоваться уже нереально, да и под чистым терминалом его не запустишь.
Выбор пал на Posting. Это TUI клиент со всем необходимым функционалом.
Так он и прижился, сейчас если что-то нужно отладить или потестить у меня есть эта замечательная тулза. Не перегружена хуйнёй и дружелюбная. Есть все самое необходимое, даже мышкой можно тыкать. Ну и сессии сохраняет со всеми запросами.
Ставится элементарно:
curl -LsSf https://astral.sh/uv/install.sh | sh
uv tool install --python 3.13 posting
uv — это современный инструмент для Python, который объединяет в себе функционал менеджера пакетов и виртуальных окружений, задумывается как замена pip + venv/virtualenv + pip-tools.
Для хакерменов прям мастхев. Забирай в копилку, годнота!
—
Please open Telegram to view this post
    VIEW IN TELEGRAM
  Вечный вопрос — как сохранить правила iptables?
ㅤ
Вот навтыкал ты всяких безумных правил и цепочек через командную строку, радуешься. А после ребута все твои поделки к хуям улетучиваются.
И каждый раз ты идешь гуглить, как это решить. Жиза. Каждый раз, как в первый раз.
А делается всё просто, двумя командами:
Ладно, спиздел, нужно еще доставить:
Эта хуёвина автоматом создает файлы
А если у тебя длинный писюн, можно и от первых двух команд избавиться.
😀 😃 😄 😁 😅 😂 🤣 😊 
😇 🙂 🙃 😉 😌 😍 🥰 😘 
😗 😙 😚 😋 😛 😝 😜 🤪 
🤨 🧐 🤓 😎 🤩 🥳 😏 😒 
Просто открываешь напрямую файл
После этого не забываем дёрнуть слона за хобот:
Либо по старинке, добавляешь правила через терминал и затем делаешь:
Но в этом случае файлы
Саммари
1. Ставим
2. Пиздярим правила в терминале
3. Применяем
Как делаю я? У меня писюн длинный, поэтому сразу вношу все необходимое руками в
Такие дела, изучай.
🛠  #linux #security #iptables
—
✅  @bashdays ✅  @linuxfactory ✅  @blog
ㅤ
Вот навтыкал ты всяких безумных правил и цепочек через командную строку, радуешься. А после ребута все твои поделки к хуям улетучиваются.
И каждый раз ты идешь гуглить, как это решить. Жиза. Каждый раз, как в первый раз.
А делается всё просто, двумя командами:
sudo iptables-save > /etc/iptables/rules.v4
sudo ip6tables-save > /etc/iptables/rules.v6
Ладно, спиздел, нужно еще доставить:
sudo apt install iptables-persistent
Эта хуёвина автоматом создает файлы
/etc/iptables/rules.v4,  /etc/iptables/rules.v6 и будет применять их при загрузке.А если у тебя длинный писюн, можно и от первых двух команд избавиться.
Просто открываешь напрямую файл
rules.v4/rules.v6 и прописываешь туда все свои хотелки. После этого не забываем дёрнуть слона за хобот:
systemctl restart netfilter-persistent
Либо по старинке, добавляешь правила через терминал и затем делаешь:
sudo netfilter-persistent save
Но в этом случае файлы
rules.v4/rules.v6 будут перезатерты, имей это ввиду если лез в них своими руками.Саммари
1. Ставим
iptables-persistent2. Пиздярим правила в терминале
3. Применяем
netfilter-persistent saveКак делаю я? У меня писюн длинный, поэтому сразу вношу все необходимое руками в
rules.v4/rules.v6, хотя это не есть бест-практика, не делай так.Такие дела, изучай.
—
Please open Telegram to view this post
    VIEW IN TELEGRAM
  1 66
  Вчера в посте я упомянул конфиги 
ㅤ
Давай посмотрим что в других дистрибутивах происходит.
Debian / Ubuntu
Основной пакет → iptables-persistent / netfilter-persistent
RHEL / CentOS / Rocky / Alma (iptables-services)
Fedora (новые версии)
По умолчанию использует firewalld (поверх nftables). Если ставишь iptables-services:
Arch Linux / Manjaro
Из коробки iptables не сохраняет правила, обычно юзеры делают сами:
OpenSUSE / SLES
По умолчанию тоже firewalld, если ставить пакет iptables, правила обычно хранят в:
Astra Linux (Смоленск, Орёл и др. редакции)
Астра базируется на Debian, поэтому у неё схема как у Debian/Ubuntu:
РЕД ОС (RedOS)
RedOS базируется на RHEL/CentOS, поэтому там всё по «редхэту»:
Вроде основное осветил, если что-то проебал, забыл, затроил — пиши в комменты, поправим.
А вообще при возможности пользуйся облачным фаерволом, если страшно конфигурять iptables или руки растут из жопы.
🛠  #linux #security #iptables
—
✅  @bashdays ✅  @linuxfactory ✅  @blog
iptables лишь для части дистрибутивов, Alex эту ситуацию в комментариях подметил верно.ㅤ
Давай посмотрим что в других дистрибутивах происходит.
Debian / Ubuntu
Основной пакет → iptables-persistent / netfilter-persistent
/etc/iptables/rules.v4
/etc/iptables/rules.v6
RHEL / CentOS / Rocky / Alma (iptables-services)
/etc/sysconfig/iptables
/etc/sysconfig/ip6tables
Fedora (новые версии)
По умолчанию использует firewalld (поверх nftables). Если ставишь iptables-services:
/etc/sysconfig/iptables
/etc/sysconfig/ip6tables
Arch Linux / Manjaro
Из коробки iptables не сохраняет правила, обычно юзеры делают сами:
/etc/iptables/iptables.rules
/etc/iptables/ip6tables.rules
OpenSUSE / SLES
По умолчанию тоже firewalld, если ставить пакет iptables, правила обычно хранят в:
/etc/sysconfig/iptables
Astra Linux (Смоленск, Орёл и др. редакции)
Астра базируется на Debian, поэтому у неё схема как у Debian/Ubuntu:
/etc/iptables/rules.v4
/etc/iptables/rules.v6
РЕД ОС (RedOS)
RedOS базируется на RHEL/CentOS, поэтому там всё по «редхэту»:
/etc/sysconfig/iptables
/etc/sysconfig/ip6tables
Вроде основное осветил, если что-то проебал, забыл, затроил — пиши в комменты, поправим.
А вообще при возможности пользуйся облачным фаерволом, если страшно конфигурять iptables или руки растут из жопы.
—
Please open Telegram to view this post
    VIEW IN TELEGRAM
  Мне прилетела интересная задача.
🔤 🔤 🔥 🔤 🔤 🔤 🔤 
ТЗ: Сервер без GUI, к нему подключают usb-диск с NTFS и начинается автоматическое архивирование. Логин не требуется, сообщения выдаются на tty1.
Использовать будем штатные средства
1. Подключаем диск, и запоминаем UUID.
здесь FLASH - метка диска
ㅤ
создадим каталог для монтирования:
Дальше создаем 4 файла. Чтобы было проще, в каждом файле в начале коммент с названием файла.
После создания:
Как это работает:
При подключении диска usb-диска срабатывает UDEV правило
Сама
Если все условия совпадают,
Если используется архивирование с чередованием дисков - просто сделайте у дисков одинаковые метки:
👆 Если в mount не указать опцию
👆 Запустить скрипт через UDEV даже в фоне не получится, поскольку система вырубит его через 5 сек.
🛠  #linux #bash
—
✅  @bashdays ✅  @linuxfactory ✅  @blog
ТЗ: Сервер без GUI, к нему подключают usb-диск с NTFS и начинается автоматическое архивирование. Логин не требуется, сообщения выдаются на tty1.
sudo apt install ntfs-3g # для монтирования ntfs
Использовать будем штатные средства
udev + systemd .1. Подключаем диск, и запоминаем UUID.
lsblk -f
sdc
└─sdc1
ntfs FLASH
1234567890ABCDEF
здесь FLASH - метка диска
1234567890ABCDEF - UUIDㅤ
создадим каталог для монтирования:
mkdir -p /media/usb_ntfs; chmod 777 /media/usb_ntfs
Дальше создаем 4 файла. Чтобы было проще, в каждом файле в начале коммент с названием файла.
После создания:
sudo systemctl daemon-reload
Как это работает:
При подключении диска usb-диска срабатывает UDEV правило
99-usb-mount.rules и пытается запустить службу autousbbackup.serviceautousbbackup.service пытается запустить media-usb_ntfs.mount, поскольку жестко он нее зависит.Сама
media-usb_ntfs.mount запустится только в том случае, если UUID диска будет 1234567890ABCDEF.Если все условия совпадают,
autousbbackup.service запустит скрипт autousbbackup.sh, внутри которого Вы напишите копирование или синхронизацию данных в каталог /media/usb_ntfs.Если используется архивирование с чередованием дисков - просто сделайте у дисков одинаковые метки:
sudo umount /dev/sdXN # где /dev/sdXN имя вашего NTFS-раздела.
sudo ntfslabel --new-serial=1234567890ABCDEF /dev/sdXN #задайте UUID
👆 Если в mount не указать опцию
nofail система будет тормозить при загрузке.👆 Запустить скрипт через UDEV даже в фоне не получится, поскольку система вырубит его через 5 сек.
#/etc/udev/rules.d/99-usb-mount.rules
SUBSYSTEM=="block", KERNEL=="sd*", ACTION=="add", TAG+="systemd", ENV{SYSTEMD_WANTS}="autousbbackup.service"
#/etc/systemd/system/media-usb_ntfs.mount
[Unit]
Description=Mount USB NTFS by UUID
[Mount]
What=/dev/disk/by-uuid/1234567890ABCDEF
Where=/media/usb_ntfs
Type=ntfs-3g
Options=defaults,big_writes,nofail,uid=1000,gid=1000
[Install]
WantedBy=multi-user.target
#/etc/systemd/system/autousbbackup.service
[Unit]
Description=Simple service autousbbackup
Requires=media-usb_ntfs.mount
After=media-usb_ntfs.mount
[Service]
Type=simple
ExecStart=/root/work/autousbbackup/autousbbackup.sh
[Install]
WantedBy=multi-user.target
#!/bin/bash
#/root/work/autousbbackup/autousbbackup.sh
declare -x PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
declare MOUNT_POINT=/media/usb_ntfs
declare TTY=/dev/tty1
declare DF='%(%Y%m%d-%H%M%S)T' #date format
declare LOG="$0.log"
exec &>"$LOG" # only one backup log
#exec &>>"$LOG" # all backups log
printf "$DF %s\n" -1 "START BACKUP"|tee "$TTY"
##############################################
sleep 3 # backup emulation
##############################################linux
printf "$DF %s\n" -1 "END BACKUP"|tee "$TTY"
#suicide, because service require mount point
systemd-mount --umount "$MOUNT_POINT"
—
Please open Telegram to view this post
    VIEW IN TELEGRAM
  6 77
  Как оказалось многие не знают, как нативным 
ㅤ
Все довольно просто и очевидно. Нужно сделать бутерброд.
В
1. Первая строка запускает скрипт в начале минуты (00:00, 00:01, 00:02…)
2. Вторая строка — ждёт 30 секунд и запускает скрипт (00:00:30, 00:01:30, 00:02:30…).
Тут и получаем шаг в 30 секунд, именно через 2 вызова.
Костыльно? Ага! Но порой не хочется ебаться с таймерами и сделать все по-быстрому. Как вариант, вполне годный. Аналогично можно городить и другие интервалы.
Минусы подхода
⚪  Нет гарантии точности. Если первый запуск скрипта будет работать дольше, чем пауза (
⚪  Мусор в crontab. Для мелкого интервала надо плодить много строк.
⚪  Нет гибкой логики.
Где это полезно
⚪  Лёгкие скрипты мониторинга (ping, проверка статуса).
⚪  Хаотизация нагрузки (например, 
⚪  Если 
А как работать с таймерами ищи по тегу #systemd, много про это писал.
🛠  #linux #cron #systemd
—
✅  @bashdays ✅  @linuxfactory ✅  @blog
cron (без systemd timers) запускать скрипты с интервалом в 30 секунд, без модификации самого скрипта.ㅤ
Все довольно просто и очевидно. Нужно сделать бутерброд.
Cron исторически работает только с минутной точностью. В crontab нельзя написать «каждые 10 секунд» или «раз в 30 секунд». Для этого обычно использую systemd timers или отдельный демонический скрипт с while true; sleep ...
В
crontab строка запускается раз в минуту. Но внутри можно поставить sleep — задержку перед запуском. Таким образом мы получим несколько запусков в рамках одной минуты.* * * * * /usr/local/sbin/bashdays.sh
* * * * * sleep 30; /usr/local/sbin/bashdays.sh
1. Первая строка запускает скрипт в начале минуты (00:00, 00:01, 00:02…)
2. Вторая строка — ждёт 30 секунд и запускает скрипт (00:00:30, 00:01:30, 00:02:30…).
Тут и получаем шаг в 30 секунд, именно через 2 вызова.
Костыльно? Ага! Но порой не хочется ебаться с таймерами и сделать все по-быстрому. Как вариант, вполне годный. Аналогично можно городить и другие интервалы.
Минусы подхода
sleep), запуски могут наложиться.Где это полезно
sleep $((RANDOM % 60)) для рассинхрона).systemd timers или другие планировщики недоступны (например, в ограниченных окружениях или старых системах).А как работать с таймерами ищи по тегу #systemd, много про это писал.
—
Please open Telegram to view this post
    VIEW IN TELEGRAM
  Как не стремись к автоматизации, всегда найдется какой-нибудь легаси сервис, который требует ручного обслуживания.
Был у меня такой сервис и работал он только тогда, когда все его файлы и каталоги принадлежали определенному пользователю.
ㅤ
Доступ к сервису имели многие, поэтому люди порой троили и запускали команды в каталоге сервиса от root. Сервису было на это поебать, но до момента его перезапуска.
Обычно это чинилось очень легко, через
Казалось бы, есть масса способов предотвратить такие ошибки: правильные права на файлы, ACL’ы, SELinux.
Но веселья в этом мало! Поэтому я решил заебенить свой собственный мониторинг файловой системы. Скиллов то предостаточно, хули нет.
Спойлер:Я залез в кроличью нору и знатно так хлебнул гавна.  
Попытка намбер 1
В Linux есть API под названием fanotify, с его помощью можно получать события о действиях с файлами в юзерспейс.
Всё просто: инициализируем fanotify_init, указываем каталоги через fanotify_mark и читаем события из дескриптора.
Но тут же вылез огромный хуй:
- нельзя отслеживать каталоги рекурсивно (только целый маунт)
-
Решение вполне рабочее, но громоздкое. Я такие не люблю, этож думать надо, вайбкодингом не решается.
Попытка намбер 2
Вспоминаем что есть
В
То есть единая точка входа. Там можно отлавливать события и фильтровать их, не гоняя лишние переключения контекста.
Но и тут вылез хуй:
-
  
- фильтрацию приходится писать прямо в
Да блядь…
Как я победил рекурсивный обход
Чтобы понять, что именно меняется в каталоге сервиса, пришлось использовать структуру
Но так как в
На практике этого вполне достаточно. Глубина каталогов мне известна. Ну и конечно, пришлось аккуратно работать с RCU-локами, чтобы дерево не поменялось в момент обхода.
Как можно улучшить
В идеале использовать не VFS-хуки, а LSM hooks (Linux Security Module).
Они стабильнее, понятнее и позволяют сразу работать с путями. Там можно красиво получить path и сразу преобразовать его в строку, а потом делать поиск подстроки.
Но в моём ядре этих хуков не было, хуй знает почему, видимо дистрибутив слишком древний. Надо попробовать на новых, чем черт не шутит.
Итоги
Эта поделка как и предполагалась, погрузила меня в печаль, душевные страдания, НО стала отличным тренажером для прокачки:
- Внутренностей Linux ядра
- Работы с eBPF
- И кучу другого с kernel-space
Информации по нему много, но вся она разбросана. Собрать всё это в кучу было отдельным квестом.
Мораль?
Иногда самое простое решение — это
🛠  #linux #debug #dev
—
✅  @bashdays ✅  @linuxfactory ✅  @blog
Был у меня такой сервис и работал он только тогда, когда все его файлы и каталоги принадлежали определенному пользователю.
ㅤ
Доступ к сервису имели многие, поэтому люди порой троили и запускали команды в каталоге сервиса от root. Сервису было на это поебать, но до момента его перезапуска.
Обычно это чинилось очень легко, через
chown -R. Все это знали и никого это не смущало. Короче костыль ебаный.Казалось бы, есть масса способов предотвратить такие ошибки: правильные права на файлы, ACL’ы, SELinux.
Но веселья в этом мало! Поэтому я решил заебенить свой собственный мониторинг файловой системы. Скиллов то предостаточно, хули нет.
Спойлер:
Попытка намбер 1
В Linux есть API под названием fanotify, с его помощью можно получать события о действиях с файлами в юзерспейс.
Всё просто: инициализируем fanotify_init, указываем каталоги через fanotify_mark и читаем события из дескриптора.
Но тут же вылез огромный хуй:
- нельзя отслеживать каталоги рекурсивно (только целый маунт)
-
anotify даёт только PID процесса, который что-то сделал. А чтобы узнать UID/GID — нужно лезть в /proc/<pid>/status. То есть на каждое событие приходится открывать и парсить файлы в /proc.Решение вполне рабочее, но громоздкое. Я такие не люблю, этож думать надо, вайбкодингом не решается.
Попытка намбер 2
Вспоминаем что есть
eBPF. Это штука позволяет запускать программы прямо в ядре Linux. Они компилируются в байткод, проходят проверку, а потом гоняются через JIT почти с нативной скоростью.Что такое eBPF можешь почитать тут и тут.
В
eBPF заебись то, что можно цепляться за разные функции ядра. Например, можно подцепиться к vfs_mkdir или vfs_create — это общий слой для работы с файлами.То есть единая точка входа. Там можно отлавливать события и фильтровать их, не гоняя лишние переключения контекста.
Но и тут вылез хуй:
-
kprobes на функции VFS нестабильны, в новых ядрах сигнатуры могут меняться или функции вообще исчезнуть.- фильтрацию приходится писать прямо в
eBPF, а там свои ограничения. Нет бесконечных циклов, стек всего ~512 байт.Да блядь…
Как я победил рекурсивный обход
Чтобы понять, что именно меняется в каталоге сервиса, пришлось использовать структуру
dentry и подниматься по дереву до родителя.Но так как в
eBPF нельзя сделать «бесконечный» цикл, я ограничил глубину с помощью MAX_DEPTH.На практике этого вполне достаточно. Глубина каталогов мне известна. Ну и конечно, пришлось аккуратно работать с RCU-локами, чтобы дерево не поменялось в момент обхода.
➡️ Кусок кода в первом комментарии, сюда не влез.
Как можно улучшить
В идеале использовать не VFS-хуки, а LSM hooks (Linux Security Module).
Они стабильнее, понятнее и позволяют сразу работать с путями. Там можно красиво получить path и сразу преобразовать его в строку, а потом делать поиск подстроки.
Но в моём ядре этих хуков не было, хуй знает почему, видимо дистрибутив слишком древний. Надо попробовать на новых, чем черт не шутит.
Итоги
Эта поделка как и предполагалась, погрузила меня в печаль, душевные страдания, НО стала отличным тренажером для прокачки:
- Внутренностей Linux ядра
- Работы с eBPF
- И кучу другого с kernel-space
eBPF — мощнейший инструмент, но очень тонкий. Ошибёшься — будешь выебан в жопу.
Информации по нему много, но вся она разбросана. Собрать всё это в кучу было отдельным квестом.
Мораль?
Иногда самое простое решение — это
chown -R. Но куда интереснее — написать свой велосипед и заглянуть в кроличью нору Linux ядра.—
Please open Telegram to view this post
    VIEW IN TELEGRAM
  9 47
  Настройка core dump в Docker
Цель этого поста — дать тебе общее руководство по включению и сбору core dumps для приложений, работающих в docker контейнере.
Настраиваем хост для сохранения дампов
Для начала надо сконфигурировать хостовую машину, чтобы сохранять такие дампы в нужное место. Нет, не в жопу.
ㅤ
Универсальный способ — задать шаблон core pattern. Шаблон определяет путь и информацию о процессе который наебнулся.
Кратенько:
Как вариант, можно настроить host изнутри контейнера через CMD или ENTRYPOINT. Но контейнер в этом случае должен запускаться в privileged mode, что хуева с точки зрения безопасности.
Пример хуёвого приложения
После компиляции и запуска, приложение наебнется с ошибкой.
Пишем Dockerfile для этого приложения
Запускаем контейнер с нужными опциями:
Разбираем опции:
    
    
Здесь важно: путь
После того как приложение наебнется, core dump будет сохранён на хостовой машине в директории
Анализируем дамп с помощью gdb
Такс, мы получили core dump и он у нас лежит на хостовой машине, но рекомендуется открыть его внутри контейнера. Контейнер должен быть из того же образа, в котором компилилось приложение.
Это поможет убедиться, что все зависимости (библиотеки и прочая хуитень) доступны.
Если в образе нет исходного кода, можно допом смаунтить исходники:
Теперь внутри контейнера запускаем:
После загрузки дампа можно выполнить команду
Команда поможет определить, где именно произошел факап.
Давай быстренько разберем ошибку.
В исходнике было:
То есть ошибка здесь не баг компиляции или рантайма, а намеренно вставленный вызов
Если у тебя docker-compose, то все флаги (
Хуй знает чё еще написать, завтра тему дебага продолжим, чет в конце года много траблшутинга навалило разнообразного.
🛠  #linux #debug #dev
—
✅  @bashdays ✅  @linuxfactory ✅  @blog
Цель этого поста — дать тебе общее руководство по включению и сбору core dumps для приложений, работающих в docker контейнере.
Настраиваем хост для сохранения дампов
Для начала надо сконфигурировать хостовую машину, чтобы сохранять такие дампы в нужное место. Нет, не в жопу.
ㅤ
Универсальный способ — задать шаблон core pattern. Шаблон определяет путь и информацию о процессе который наебнулся.
echo '/tmp/core.%e.%p' | sudo tee /proc/sys/kernel/core_pattern
Кратенько:
%e — имя процесса %p — pid процессаБолее подробно о конфигурации core pattern можешь почитать в man-странице ядра Linux.
Как вариант, можно настроить host изнутри контейнера через CMD или ENTRYPOINT. Но контейнер в этом случае должен запускаться в privileged mode, что хуева с точки зрения безопасности.
Пример хуёвого приложения
#include <cstdlib>
void foo() {
std::abort();
}
int main() {
foo();
return 0;
}
После компиляции и запуска, приложение наебнется с ошибкой.
Пишем Dockerfile для этого приложения
FROM ubuntu:22.04
# Install tools
RUN apt update \
&& apt -y install \
build-essential \
gdb \
&& rm -rf /var/lib/apt/lists/*
# Build the application
COPY ./ /src/
WORKDIR /src/
RUN g++ main.cpp -o app
CMD ["/src/app"]
Тот кто в теме, сможет его прочитать и понять. Если не понятно, гугли и разбирай, качнешь свой девопсовый скиллз.
Запускаем контейнер с нужными опциями:
docker run \
--init \
--ulimit core=-1 \
--mount type=bind,source=/tmp/,target=/tmp/ \
application:latest
Разбираем опции:
--init — гарантирует корректную обработку сигналов внутри контейнера--ulimit — устанавливает неограниченный размер core dump для процессов внутри контейнера--mount — монтирует /tmp из хоста в контейнер, чтобы дампы, создаваемые внутри контейнера, были доступны после его остановки или удаленияЗдесь важно: путь
source на хосте должен совпадать с тем, который задан в шаблоне core pattern.После того как приложение наебнется, core dump будет сохранён на хостовой машине в директории
/tmp. ls /tmp/core*
# /tmp/core.app.5
Анализируем дамп с помощью gdb
Такс, мы получили core dump и он у нас лежит на хостовой машине, но рекомендуется открыть его внутри контейнера. Контейнер должен быть из того же образа, в котором компилилось приложение.
Это поможет убедиться, что все зависимости (библиотеки и прочая хуитень) доступны.
docker run \
-it \
--mount type=bind,source=/tmp/,target=/tmp/ \
application:latest \
bash
Если в образе нет исходного кода, можно допом смаунтить исходники:
docker run \
-it \
--mount type=bind,source=/tmp/,target=/tmp/ \
--mount type=bind,source=<путь_к_исходникам_на_хосте>,target=/src/ \
application:latest \
bash
Теперь внутри контейнера запускаем:
gdb app /tmp/core.app.5
После загрузки дампа можно выполнить команду
bt (backtrace), чтобы увидеть трассировку стека:(gdb) bt
#0 __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:51
#1 0x00007f263f378921 in __GI_abort () at abort.c:79
#2 0x000055f9a9d16653 in foo() ()
#3 0x000055f9a9d1665c in main ()
Команда поможет определить, где именно произошел факап.
Давай быстренько разберем ошибку.
#0 и #1 показывают, что процесс получил сигнал 6 (SIGABRT) и завершился через abort()#2 — вызов произошёл внутри функции foo()#3 — main() вызвал foo()В исходнике было:
void foo() {
    std::abort();
}То есть ошибка здесь не баг компиляции или рантайма, а намеренно вставленный вызов
std::abort(), который и приводит к аварийному завершению и генерации core dump.Если у тебя docker-compose, то все флаги (
--init, --ulimit, --mount и т.д.) применимы и для него. То есть отладку можно легко адаптировать.Хуй знает чё еще написать, завтра тему дебага продолжим, чет в конце года много траблшутинга навалило разнообразного.
—
Please open Telegram to view this post
    VIEW IN TELEGRAM
  5 45
  Здесь: я как-то поднимал проблему с торможением 1c на postgres.
🔤 🔤 🔥 🔤 🔤 🔤 🔤 
Благодаря нашему коллеге @ovchinnikovmy, дело сдвинулось с мертвой точки. Спасибо ему большое за консультации и рекомендации по PG.
ㅤ
Мы начали попытки оптимизировать работу postgres для нашей задачи. И сразу столкнулись с проблемой. Ну, оптимизировали. А насколько?
Улучшение есть, а кто был виноват в тормозах PG или 1С?
Все может прекрасно работать в тестах, и становится колом, когда идет интенсивная работа в нескольких базах одновременно. Где горлышко бутылки - число ядер, частота или скорость диска, или может пора памяти добавить?
Там маленькая конторка. Фактически один сервак. Не будешь же zabbix ради этого ставить.
Онлайн можно посмотреть через
Остановился на пакете
Он собирает статистику каждые 10 сек по двум пользователям postgres (PG) и usr1cv83 (1С) в csv-лог (разделитель пробел, но это можно исправить).
Поскольку лог текстовый, дальше его можно вертеть с помощью awk/sort или просто в LibreOffice Calc.
pidstat ключи:
-r - память
-l - командная строка процесса
-d - диск
-h - табличный режим
-H - время unix
-U - username
-u - проц
gawk ключи:
🛠  #debug #linux
—
✅  @bashdays ✅  @linuxfactory ✅  @blog
Благодаря нашему коллеге @ovchinnikovmy, дело сдвинулось с мертвой точки. Спасибо ему большое за консультации и рекомендации по PG.
ㅤ
Мы начали попытки оптимизировать работу postgres для нашей задачи. И сразу столкнулись с проблемой. Ну, оптимизировали. А насколько?
Улучшение есть, а кто был виноват в тормозах PG или 1С?
Все может прекрасно работать в тестах, и становится колом, когда идет интенсивная работа в нескольких базах одновременно. Где горлышко бутылки - число ядер, частота или скорость диска, или может пора памяти добавить?
Там маленькая конторка. Фактически один сервак. Не будешь же zabbix ради этого ставить.
Онлайн можно посмотреть через
nmon, top/htop. nmon даже позволяет записывать данные в лог, и есть программа, которая позволяет генерить html с отчетами, но там все интегрально. По системе. А хочется по процессам.Остановился на пакете
sysstat. Это такой консольный zabbix. Он позволяет собирать статистику по процессам. Анализировать можно память, проц, диск, стэк. Причем по каждому PID в отдельности и прямо в консоли. В общем, все, что нужно. Для большего удобства я набросал скрипт.#!/bin/bash
# 20251005
# apt install sysstat gawk
# работа с 9 до 18, запись с 8:30 до 18:30
# запуск через cron
# 30 8 * * * /root/work/stat/stat.sh &
declare -x PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
declare -i INTERVAL_SEC=10
declare -i COUNT=3600 # итого 10 часов
declare -i WEEK_DAY;printf -v WEEK_DAY "%(%-u)T"
declare LOG="$0_${WEEK_DAY}.csv"
pidstat -r -l -d -H -h -U -u $INTERVAL_SEC $COUNT |
gawk 'NR<4;$2=="usr1cv83"||$2=="postgres"{$1=strftime("%Y%m%d_%H%M%S",$1);print}'>"$LOG"
Он собирает статистику каждые 10 сек по двум пользователям postgres (PG) и usr1cv83 (1С) в csv-лог (разделитель пробел, но это можно исправить).
Поскольку лог текстовый, дальше его можно вертеть с помощью awk/sort или просто в LibreOffice Calc.
pidstat ключи:
-r - память
-l - командная строка процесса
-d - диск
-h - табличный режим
-H - время unix
-U - username
-u - проц
gawk ключи:
NR<4 - заголовок (легенда) из трех строк$2=="usr1cv83"||$2=="postgres" - фильтрация по username$1=strftime("%Y%m%d_%H%M%S",$1) - удобный формат времени.LOG="$0_${WEEK_DAY}.csv" - Недельная ротация. По одному на день.—
Please open Telegram to view this post
    VIEW IN TELEGRAM
  Сколько линуксоида не корми, он все равно на винду поглядывает.
Накопал я тут тебе WinBoat.
Эта штука запускает Windows-приложения прямо в Linux с максимально «нативной» интеграцией: окна приложений — как обычные X/Wayland-окна, общий доступ к файлам и даже полноценный Windows-десктоп.
Что в коробке
- Запустит любое приложение, которое работает в Windows — в том числе коммерческие программы.
Ради прикола запустил Excel, ну что сказать, минимум приседаний и танцев с бубном. Работает прям отлично.
- Окна приложений интегрируются в рабочий стол Linux, не просто RDP в едином окне, а RemoteApp-композитинг.
ㅤ
- Автоматические инсталляции через GUI — выбираешь параметры, остальное делает WinBoat.
- Файловая интеграция, домашний каталог монтируется в винду.
- Полный Windows-десктоп по запросу (если нужно работать внутри полноценной виртуальной машины).
Что под капотом
Это Electron-приложение + сопутствующий «guest server». Windows запускается как VM внутри контейнера Docker. А между хостом и гостем общаются через WinBoat Guest Server.
Затем для прорисовки отдельных окон используется FreeRDP + Windows RemoteApp — это и даёт ложное ощущение нативности.
Штука интересная возможно бы я ее использовал, если бы плотно сидел на линуксе.
Так что приглядись, глядишь установишь в хозяйстве приживется.
🛠  #утилиты #utilites #linux
—
✅  @bashdays ✅  @linuxfactory ✅  @blog
Накопал я тут тебе WinBoat.
Эта штука запускает Windows-приложения прямо в Linux с максимально «нативной» интеграцией: окна приложений — как обычные X/Wayland-окна, общий доступ к файлам и даже полноценный Windows-десктоп.
Что в коробке
- Запустит любое приложение, которое работает в Windows — в том числе коммерческие программы.
Ради прикола запустил Excel, ну что сказать, минимум приседаний и танцев с бубном. Работает прям отлично.
- Окна приложений интегрируются в рабочий стол Linux, не просто RDP в едином окне, а RemoteApp-композитинг.
ㅤ
- Автоматические инсталляции через GUI — выбираешь параметры, остальное делает WinBoat.
- Файловая интеграция, домашний каталог монтируется в винду.
- Полный Windows-десктоп по запросу (если нужно работать внутри полноценной виртуальной машины).
Что под капотом
Это Electron-приложение + сопутствующий «guest server». Windows запускается как VM внутри контейнера Docker. А между хостом и гостем общаются через WinBoat Guest Server.
Затем для прорисовки отдельных окон используется FreeRDP + Windows RemoteApp — это и даёт ложное ощущение нативности.
Штука интересная возможно бы я ее использовал, если бы плотно сидел на линуксе.
Так что приглядись, глядишь установишь в хозяйстве приживется.
—
Please open Telegram to view this post
    VIEW IN TELEGRAM
  Please open Telegram to view this post
    VIEW IN TELEGRAM
  Фиксим кривой Exit Code в docker
Во время работы с docker контейнером наткнулся на неочевидный код выхода (exit code).
ㅤ
Суть проблемы: Когда программа внутри контейнера падает с
То есть контейнер маскирует реальную причину падения приложения. Соответственно дальнейший дебаг не имеет смысла, потому что код выхода не соответствует действительности.
Давай проверим:
Пишем Dockerfile:
Собираем и запускаем:
А на выходе у нас код:
В примере выше код выхода — 139 = 128 + 11, где
Чтобы это пофиксить, нужно захерачить хак:
После этого контейнер будет возвращать корректный код
Вариант рабочий, но костыльный. Правильнее использовать ключ
Если запустить контейнер с флагом
Почему init все починил?
Давай копнём глубже. В Linux процесс с PID 1 (init) имеет нестандартную семантику сигналов:
- Если у PID 1 для сигнала стоит действие «по умолчанию» (никакого обработчика), то сигналы с действием
- PID 1 должен забирать зомби-процессы (делать
- PID 1 обычно пробрасывает сигналы дальше — тому «настоящему» приложению, которое оно запускает.
Когда мы запускаем контейнер без
Наш сценарий с
Для обычного процесса с PID ≠ 1 это приводит к завершению с кодом
Ну а дальше вступают в силу детали реализации рантайма/сишной библиотеки, как именно контейнерный рантайм считывает статус.
На практике это может приводить к тому, что ты видишь
И тут проблема не в docker, а в том, что приложение неожиданно оказалось в роли init-процесса и попало под его особые правила.
Вот и вся наука. Изучай.
🛠  #docker #devops #linux #debug
—
✅  @bashdays ✅  @linuxfactory ✅  @blog
Во время работы с docker контейнером наткнулся на неочевидный код выхода (exit code).
Про exit codes (коды выхода) писал тут
ㅤ
Суть проблемы: Когда программа внутри контейнера падает с
abort(), Docker возвращает неправильный код выхода. Вместо ожидаемого 134 (128 + SIGABRT), контейнер отдаёт 139 (128 + SIGSEGV).То есть контейнер маскирует реальную причину падения приложения. Соответственно дальнейший дебаг не имеет смысла, потому что код выхода не соответствует действительности.
Давай проверим:
#include <cstdlib>
int main() {
std::abort();
return 0;
}
Пишем Dockerfile:
FROM ubuntu:22.04
RUN apt-get update \
&& apt-get -y install \
build-essential \
&& rm -rf /var/lib/apt/lists/*
COPY ./ /src/
WORKDIR /src/
RUN g++ main.cpp -o app
WORKDIR /
CMD ["/src/app"]
Собираем и запускаем:
docker build -f ./Dockerfile -t sigabort_test:latest .
docker run --name test sigabort_test:latest ; echo $?
А на выходе у нас код:
139.В примере выше код выхода — 139 = 128 + 11, где
11 соответствует SIGSEGV (ошибка сегментации), а не 134 = 128 + 6, что был бы SIGABRT (аварийное завершение).Чтобы это пофиксить, нужно захерачить хак:
CMD ["bash", "-c", "/src/app ; exit $(echo $?)"]
docker run --name test sigabort_test:latest ; echo $?
bash: line 1: 6 Aborted /src/app
134
После этого контейнер будет возвращать корректный код
134.Вариант рабочий, но костыльный. Правильнее использовать ключ
--init.Если запустить контейнер с флагом
--init, используя исходную команду CMD ["/src/app"], мы получим ожидаемый 134 код. Что нам и нужно.docker run --init --name test sigabort_test:latest ; echo $?
134
Почему init все починил?
Давай копнём глубже. В Linux процесс с PID 1 (init) имеет нестандартную семантику сигналов:
- Если у PID 1 для сигнала стоит действие «по умолчанию» (никакого обработчика), то сигналы с действием
terminate игнорируются ядром. Это сделано, чтобы случайным SIGTERM/SIGINT нельзя было «уронить» init.- PID 1 должен забирать зомби-процессы (делать
wait() за умершими детьми). Если этого не делать, накопятся зомби.- PID 1 обычно пробрасывает сигналы дальше — тому «настоящему» приложению, которое оно запускает.
Когда мы запускаем контейнер без
--init, приложение становится PID 1.Большинство обычных приложений (на C/C++/Go/Node/Java и т.д.) не написаны как «инит-системы», они не настраивают обработку всех сигналов, не занимаются «реапингом» детей и не пробрасывают сигналы. В результате вылазиют баги.
Наш сценарий с
abort() (который поднимает SIGABRT) упирается именно в правила для PID 1. abort() внутри процесса поднимает SIGABRT.Для обычного процесса с PID ≠ 1 это приводит к завершению с кодом
128 + 6 = 134. Но если процесс — PID 1, ядро игнорирует «терминирующие» сигналы при действии по умолчанию. В результате стандартные ожидания вокруг SIGABRT ломаются.Ну а дальше вступают в силу детали реализации рантайма/сишной библиотеки, как именно контейнерный рантайм считывает статус.
На практике это может приводить к тому, что ты видишь
139 (SIGSEGV) вместо ожидаемого 134 (SIGABRT).И тут проблема не в docker, а в том, что приложение неожиданно оказалось в роли init-процесса и попало под его особые правила.
Вот и вся наука. Изучай.
—
Please open Telegram to view this post
    VIEW IN TELEGRAM
  5 66
  Fedora Post Install
ㅤ
Набрёл тут на Bash поделку CRIMS0NH4T.
В двух словах — делает за тебя необходимую рутину после чистой установки Fedora.
— Настройка DNF
— Установка RMP Fusion
— Установка кодеков
— GPU драйвера (Intel, Nvidia, AMD)
— Тюнинг производительности
— Оптимизация Gnome
Как я понял, версия прям ранняя альфа-альфа и активно допиливается автором.
Я бы минимально запатчил так:
Хотя
Как концепт, вполне можно форкнуть и подсмотреть какие-то штуки, возможно в своих поделках тебе что-то сгодится.
🛠  #linux #tweaks
—
✅  @bashdays ✅  @linuxfactory ✅  @blog
ㅤ
Набрёл тут на Bash поделку CRIMS0NH4T.
В двух словах — делает за тебя необходимую рутину после чистой установки Fedora.
— Настройка DNF
— Установка RMP Fusion
— Установка кодеков
— GPU драйвера (Intel, Nvidia, AMD)
— Тюнинг производительности
— Оптимизация Gnome
Как я понял, версия прям ранняя альфа-альфа и активно допиливается автором.
Я бы минимально запатчил так:
- set -u
+ set -euo pipefail
+ IFS=$'\n\t'
- readonly LOG_FILE="/tmp/crimsonhat_$(date +%Y%m%d_%H%M%S).log"
+ LOG_FILE="$(mktemp /tmp/crimsonhat.XXXXXX.log)"
+ readonly LOG_FILE
- run_with_progress() { local message="$1" shift log INFO "$message" if "$@" >/dev/null 2>&1; then return 0 else return 1 fi }
+ run_with_progress() {
+ local message="$1"; shift
+ log INFO "$message"
+ if "$@" >>"$LOG_FILE" 2>&1; then
+ return 0
+ else
+ return 1
+ fi
+}
- disk_type=$(lsblk -d -o name,rota | awk 'NR==2 {print $2}')
- primary_disk=$(lsblk -d -o name,rota | awk 'NR==2 {print $1}')
+ read -r primary_disk disk_type < <(lsblk -dn -o NAME,ROTA | head -n1)
...
- current_scheduler=$(cmd < "$scheduler_path" 2>/dev/null | grep -o '\[.*\]' | tr -d '[]')
+ current_scheduler=$(tr -d '\n' < "$scheduler_path" | sed -n 's/.*\[\(.*\)\].*/\1/p')
Хотя
set -euo pipefail тут конечно спорный вариант, можно наступить на грабли.pipefail заставляет конвейер возвращать ошибку, если любая команда в скрипте упала. Так что применив set -u автор обезопасил себя от неочевидных багов.
Как концепт, вполне можно форкнуть и подсмотреть какие-то штуки, возможно в своих поделках тебе что-то сгодится.
—
Please open Telegram to view this post
    VIEW IN TELEGRAM
  5 23
  Сегодня поделюсь историей, которая началась плохо, но закончилась хорошо.
ㅤ
🔤 🔤 🔤 🔤 🔤 🔤 🔤 
В общем, у меня домашний шлюз - mini PC на debian. А поскольку мощность для шлюза избыточна, приходится использовать ее не по прямому назначению.
Поставил туда
Сами понимаете, что каждый раз в консоли набирать
И вот сегодня я накосячил с алиасом настолько знатно, что bash упал и я покинул чат. И после этого не смог подключиться по ssh.
Если бы я правил
Думал уже придется лезть на чердак, снимать миниписюк и править файлы локально, но все оказалось гораздо проще. Оказывается есть даже два способа, чтобы поправить ситуацию.
1. Выполнить
Вместо
2. Подключиться по sftp, скачать
В общем, все сработало нормально. Алиас исправил. Все работает.
А подключился бы, как положено, при правке важных файлов двумя ssh-сессиями. И не было бы этой статьи.
Кстати, использование именно первого пункта позволяет обходить тривиальные защиты ssh (типа скриптовой двухфакторной аутентификации или использование sleepshell для пользователя, которому только разрешен проброс портов), основанные на простой замене оболочки пользователя в
При выполнении скрипта, хакер уже аутентифицирован в системе, и может просто заменить оболочку.
🛠  #linux #ssh #fix
—
✅  @bashdays ✅  @linuxfactory ✅  @blog
ㅤ
В общем, у меня домашний шлюз - mini PC на debian. А поскольку мощность для шлюза избыточна, приходится использовать ее не по прямому назначению.
Поставил туда
transmission-daemon, и рулю в консоли через transmission-remote.Сами понимаете, что каждый раз в консоли набирать
transmission-remote да еще с ключами — кнопки сотрутся, поэтому пользуюсь алиасами.И вот сегодня я накосячил с алиасом настолько знатно, что bash упал и я покинул чат. И после этого не смог подключиться по ssh.
Если бы я правил
sshd_config, я бы, конечно, подключился двумя сессиями, и все восстановил. Кто ж знал, что правка .bashrc тоже опасна.Думал уже придется лезть на чердак, снимать миниписюк и править файлы локально, но все оказалось гораздо проще. Оказывается есть даже два способа, чтобы поправить ситуацию.
1. Выполнить
ssh user@host -t /bin/shВместо
/bin/sh можно указать любую оболочку (их примерный список можно глянуть в /etc/shells). Понятно, что оболочка должна быть установлена на удаленной машине.2. Подключиться по sftp, скачать
.bashrc, локально отредактировать и залить обратно.В общем, все сработало нормально. Алиас исправил. Все работает.
А подключился бы, как положено, при правке важных файлов двумя ssh-сессиями. И не было бы этой статьи.
Кстати, использование именно первого пункта позволяет обходить тривиальные защиты ssh (типа скриптовой двухфакторной аутентификации или использование sleepshell для пользователя, которому только разрешен проброс портов), основанные на простой замене оболочки пользователя в
/etc/passwd.При выполнении скрипта, хакер уже аутентифицирован в системе, и может просто заменить оболочку.
—
Please open Telegram to view this post
    VIEW IN TELEGRAM
  5 49