Конвертируем все записи в Asterisk в mp3 формат

#!/bin/sh
#convert wav to mp3 asterisk recordings
cdrdb="asteriskcdrdb"
user="root" #пользователь субд для отчетов
pass="" #пароль пользователя субд

for i in `find /home/asterisk/monitor -type f -name "*.wav"`
do
 if [ -e "$i" ]; then
    file=`basename "$i" .wav`;
    dir=`dirname "$i"`;
    lame -h -b 32 "$i" "$dir/$file.mp3";
    rm -f "$dir/$file.wav";
    mysql -u $user -p$pass --execute='use asteriskcdrdb;UPDATE cdr SET recordingfile="'$file'.mp3" WHERE recordingfile="'$file'.wav";';
  fi
done

Как узнать класс аргумента макроса?

Довольно часто возникает необходимость генерировать разный код в зависимости от того, аргумент какого типа передан в макрос.
Например, недавно я столкнулся с тем, что аргумент может быть как замыканием, которое нужно вызвать для получения конечного значения, так и сразу значением.
В такой ситуации можно использовать свойство всех литералов class_name для реализации соотв. ветвления в макросе (playground)

macro some(arg)
  {% if arg.class_name == "ProcLiteral" %}
    {{arg}}.call
  {% else %}
    {{arg}}
  {% end %}
end

puts some(478)
puts some(->{"hello, world"})


Нужно заметить, что хотя метод «class» имплементируется базовым/«корневым» классом Object, от которого наследуются все остальные классы, работает это свойство только в рантайме (о чём явно сказано в документации). Почему для литералов сделали отдельное свойство class_name вместо того, чтобы просто переопределить «class» из родительского Object'а — мне не совсем понятно. Тем не менее подход с class_name отлично работает в макросах, как показано в примере выше.

Сброс пароля Windows Server 2012

Проверил, работает )

Сброс пароля Windows Server 2012
Можно сбросить пароль пользователя и администратора стандартными средствами Windows.

Загружаемся с установочного диска Windows Server DVD (он же является и диском восстановления).
Выбираем язык и нажимаем Далее (Next)
Выбираем Восстановление сисемы (“Repair your computer”)
Выбираем Диагностика (“Troubleshoot”)
Выбираем Командная строка (“Command Prompt”)
Выполняем команды:

cd с:\windows\system32
ren Utilman.exe Utilman.exe.old
copy cmd.exe Utilman.exe

где c — диск, на котором установлен Windows Server
Закрываем командную строку и нажимаем Продолжить(“Continue”)
Сервер загрузится и выведет logon screen. Нажимаем Windows Key + U.
Набираем net user administrator 123456
Где 123456 — пароль



Читать дальше →

Мониторинг транков asterisk

Периодически в asterisk отваливаются транки, после sip reload продолжают работать. Бывает редко, раз в месяц, раз в полгода, но неприятно. Использую такой скрипт для мониторинга
#!/bin/bash
# Задаем переменные
ALLTRUNKSMINIMUM="`/usr/sbin/asterisk -rx "sip show registry"`" # Смотрим статусы транков
ALLTRUNKS=`echo "$ALLTRUNKSMINIMUM" |grep "SIP registrations" |awk '{print $1}'` # Смотрим сколько всего транков в системе
REGTRUNKS=`/usr/sbin/asterisk -rx "sip show registry" |grep Registered |wc -l` # Зарегистрированные  транки (все транки со статусом Registered)
# Задаем условие
if [ "$REGTRUNKS" -lt "$ALLTRUNKS" ]; then # Если есть хоть один проблемный транк (В одном или более транке встретился статус отличный от Registered) то
sleep 5 # Ждем 5 секунд
echo `/usr/sbin/asterisk -rx "sip reload"` # Делаем sip reload
sleep 5 # Ждем 5 секунд
VAR=`/usr/sbin/asterisk -rx "sip show registry"` # Смотрим статусы транков после проверки
# Уведомляем на почту о том какие статусы стали после проверки
echo "$VAR" | mail -s "Мониторинг транков" your@mail.ru
# Если условие ложно (т.е. с  транками все хорошо)
else
echo “ALL STATUS OK” # Просто выводим что со всеми статусами все хорошо и ничего не делаем больше
fi

Сборка nginx 1.18 из исходников в Centos 7

  • Linux
yum check-update || sudo yum update -y
yum groupinstall -y 'Development Tools' && sudo yum install -y vim
yum install -y epel-release
yum install -y perl perl-devel perl-ExtUtils-Embed libxslt libxslt-devel libxml2 libxml2-devel gd gd-devel GeoIP GeoIP-devel
wget https://nginx.org/download/nginx-1.18.0.tar.gz && tar zxvf nginx-1.18.0.tar.gz
wget https://ftp.pcre.org/pub/pcre/pcre-8.44.tar.gz && tar xzvf pcre-8.44.tar.gz
wget https://www.zlib.net/zlib-1.2.11.tar.gz && tar xzvf zlib-1.2.11.tar.gz
wget https://www.openssl.org/source/openssl-1.1.1f.tar.gz && tar xzvf openssl-1.1.1f.tar.gz
rm -rf *.tar.gz
cd ~/nginx-1.18.0

./configure --prefix=/etc/nginx \
            --sbin-path=/usr/sbin/nginx \
            --modules-path=/usr/lib64/nginx/modules \
            --conf-path=/etc/nginx/nginx.conf \
            --error-log-path=/var/log/nginx/error.log \
            --pid-path=/var/run/nginx.pid \
            --lock-path=/var/run/nginx.lock \
            --user=nginx \
            --group=nginx \
            --build=CentOS \
            --builddir=nginx-1.18.0 \
            --with-select_module \
            --with-poll_module \
            --with-threads \
            --with-file-aio \
            --with-http_ssl_module \
            --with-http_v2_module \
            --with-http_realip_module \
            --with-http_addition_module \
            --with-http_xslt_module=dynamic \
            --with-http_image_filter_module=dynamic \
            --with-http_geoip_module=dynamic \
            --with-http_sub_module \
            --with-http_dav_module \
            --with-stream_ssl_module \
            --with-http_flv_module \
            --with-http_mp4_module \
            --with-http_gunzip_module \
            --with-http_gzip_static_module \
            --with-http_auth_request_module \
            --with-http_random_index_module \
            --with-http_secure_link_module \
            --with-http_degradation_module \
            --with-http_slice_module \
            --with-http_stub_status_module \
            --http-log-path=/var/log/nginx/access.log \
            --http-client-body-temp-path=/var/cache/nginx/client_temp \
            --http-proxy-temp-path=/var/cache/nginx/proxy_temp \
            --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp \
            --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp \
            --http-scgi-temp-path=/var/cache/nginx/scgi_temp \
            --with-mail=dynamic \
            --with-mail_ssl_module \
            --with-stream=dynamic \
            --with-stream_ssl_module \
            --with-stream_realip_module \
            --with-stream_geoip_module=dynamic \
            --with-stream_ssl_preread_module \
            --with-compat \
            --with-pcre=../pcre-8.44 \
            --with-pcre-jit \
            --with-zlib=../zlib-1.2.11 \
            --with-openssl=../openssl-1.1.1f \
            --with-openssl-opt=no-nextprotoneg \
            --with-debug


make 
sudo make install

Читаем INI-файл: что может быть проще?

  • BASH
Как ни странно, INI-файлы читаются на BASH не сложнее, чем на любом другом языке, а по мне — так даже элегантнее.

Вот вам пример, который можно сделать ещё короче, если немного покумекать:


#!/bin/bash
read_ini () {
  local file="$1"
  local arr_name="$2"
  local -A ini_conf
  while read l; do
    if [[ $l =~ ^\[([^]]+)\] ]]; then
            section=${BASH_REMATCH[1]}
    elif [[ $l =~ ^([^=[:space:]]+)[[:space:]]*=[[:space:]]*(.+)$ ]]; then
            source <(cat <<CODE
            ini_conf["${section:+${section}.}${BASH_REMATCH[1]}"]=${BASH_REMATCH[2]}
CODE
)   
    fi
  done < <(sed -r -e '/^\s*([#;].*)$/d' -e 's%^\s+%%; s%\s+$%%' "$file")
  local t=$(declare -p ini_conf)
  [[ $arr_name ]] && echo ${t/declare -A ini_conf=/declare -A ${arr_name}=} || echo "$t"
}
 
read_ini "$@"


Дерзайте и дерзновенны будете! К чему бы это я? А к тому, что source в BASH — обалденно мощная и удобная штука, намного лучше eval, вынуждающего экранировать кавычки и далеко не всегда дающего адекватные результаты. Не стесняйтесь чаще и больше пользоваться source'ом!

Собственно, именно source'ом и нужно «подобрать» вывод функции read_ini, дабы получить ассоциативный массив, имя которого передаётся вторым параметром.

Возникли вопросы? Пишите в комментариях — буду рад ответить.
За сим до новых встреч!

Как получить строку из функции на Си?

Почему-то эту тему совсем не осветили в документации Crystal, упомянув только то, что передавать и получать строки (при взаимодействии с кодом на Си) можно только как указатели на UInt8. Ни одного примера на эту тему не нашёл, поэтому на всякий случай «восполню пробел»:

lib LibC
  fun ctermid : UInt8*
end
puts String.new(LibC.ctermid)

Функция ctermid возвращает указатель на строку, которая *char с точки зрения языка Си, и она же UInt8* (т.е. Pointer(UInt8)) в языке Crystal. После выполнения функции ctermid в jбласть памяти, адресуемую полученным из функции указателем UInt8*, будет записана последовательность байт, заканчивающаяся байтом со значением 0, что является обычным для Си способом представления строк. И хотя без проблем можно было бы получить результирующую строку, попросту пройдясь в цикле до того самого «нулевого» байта, даже от этой необходимости нас избавил рантайм языка: ведь в Crystal можно создать строку напрямую из UInt8*, что, собственно, и показано в примере выше!

Вот такой простой пример. Для домашней же проработки оставляю вам статью, в которой описаны куда более интересные трюки, допустимые при работе с внешними функциями на языке Си.

За сим до свидания — и до встречи в русскоязычной телеграмм-группе, посвящённой языку Crystal:
telegram.me/crystal_ru

hex_dump строки

Время не стоит на месте, и в Crystal версии 0.26 уже есть прекрасный встроенный метод hexbytes для декодирования строки, содержащей шестнадцатеричный дамп, — в последовательность байт.

А как сделать прямое преобразование? То есть если у нас есть строка — и нужно получить её шестнадцатеричный дамп, какой метод можно использовать?

Ответ с одной стороны пока неутешителен: нет такого метода. С другой стороны, ответ слишком сложен, потому что на архитектуре Intel делать такое преобразование быстрее всего MMX-инструкциями, а значит — использовать встроенный asm-препроцессор (что, разумеется, не будет сделано разработчиками Crystal, поскольку они поддерживают «много разных архитектур» и стараются до последнего всё-таки обходиться собственными средствами языка).

Ну а с третьей стороны — как раз собственными средствами языка всё делается достаточно просто:

def hex_dump (s : String)
  s.to_slice.each_with_object(IO::Memory.new) {|b,s| s << b.to_s(16) }.to_s
end

Здесь мы сначала говорим: «с тем, что было строкой, далее работай как с указателем на побайтовый вектор» (s.to_slice), затем для каждого из байт, на которые указывает наш «слайс», вставляем их шестнадцатеричное представление в объект типа IO::Memory, созданный на лету в конструкции each_with_object. Само преобразование из «байта» в шестнадцатеричное строковое представление ('1' => 31) осуществляется функцией с наименее очевидной в данном случае семантикой: b.to_s(16).

Ну и в конце полученный объект типа IO::Memory преобразуется в строку (.to_s), после чего эта строка и становится выходным значением hex_dump'а

Кстати, это не самый эффективный способ преобразования: hex_dump(io) был бы и проще, и быстрее в случаях наподобие puts hex_dump(s). Но эту задачку я оставлю пытливым умам на домашнюю проработку :)

Простой PPTP сервер для Centos 7

  • Linux
Проверенный много раз в бою скрипт, можно брать VDS и раскатывать сервер pptp.
Все шаги выполняйте как тут выложены блоки по очереди

1.
yum -y update


2.
yum install -y epel-release
yum install -y net-tools vim
yum install -y ppp pptp pptpd pptp-setup
chkconfig pptpd on

3.
mv /etc/pptpd.conf /etc/pptpd.conf.bak
vim  /etc/pptpd.conf

Содержимое
option /etc/ppp/options.pptpd
logwtmp
localip 10.0.10.1
remoteip 10.0.10.2-254

4.
mv /etc/ppp/options.pptpd /etc/ppp/options.pptpd.bak
vim /etc/ppp/options.pptpd

Содержимое
name pptpd
refuse-pap
refuse-chap
refuse-mschap
#require-chap
#require-mschap
#require-mschap-v2
require-mppe-128
require-mschap-v2
proxyarp
lock
nobsdcomp
novj
novjccomp
nologfd
ms-dns 8.8.8.8
ms-dns 8.8.4.4

5.
mv /etc/ppp/chap-secrets /etc/ppp/chap-secrets.bak
vim /etc/ppp/chap-secrets

Содержимое
# Secrets for authentication using CHAP
# client        server  secret                  IP addresses
test * test *

6.
vim /usr/lib/sysctl.d/50-default.conf

Добавить строку
net.ipv4.ip_forward=1

7.
vim /etc/selinux/config

Заменить опцию
SELINUX=disabled

8.
firewall-cmd --permanent --add-masquerade
firewall-cmd --zone=public --add-port=1723/tcp --permanent
firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 0 -p gre -j ACCEPT
firewall-cmd --permanent --direct --add-rule ipv6 filter INPUT 0 -p gre -j ACCEPT
firewall-cmd --reload

перегружаем и пробуем подключаться по логину паролю test/test

Макросы: как задать режим компиляции извне?

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

В Crystal это делается элементарно:
  1. Задаём переменную окружения MODE_VAR локально для процесса-компилятора
    
    MODE_VAR=1 crystal src/mutating_code.cr
    

  2. Учитываем значение этой переменной во время компиляции:
    
    {% begin %}
      {% if env("MODE_VAR") != nil %}
        puts "Do something if MODE_VAR was triggered"
      {% else %}
        puts "Do anything else if MODE_VAR environment variable is absent "
      {% end %}
    {% end %}
    

Рецепт: Передача переменных простых типов по ссылке

Предположим, вы хотите написать функцию, модифицирующую значение переменной простого типа «на лету».

Если вы сделаете вот так:

def inc(cnt : Int32)
  cnt += 1
end
n = 0
inc(n)
puts n
# "0" will be printed

— ничего у вас не выйдет, поскольку в действительности значение cnt передано не по ссылке, а по значению. Если вдаваться в технические подробности, то значение переменной cnt будет скопировано на вершину стека вызова функции-«метода», а внутри этой функции будет модифицировано именно значение в стеке, а не исходная переменная cnt.

Как же правильно передать значение по ссылке?
Многословность спасёт мир:

def inc(cnt : Pointer(Int32))
  cnt.value += 1
end
n = 0
inc(pointerof(n))
puts n
# output: "1"

(Посмотреть, как работает код...)

Собственно, не