Емельянов Эдуард Владимирович (eddy_em) wrote,
Емельянов Эдуард Владимирович
eddy_em

Category:

Мои заметки с ЛОР-вики

Т.к. ЛОР-вики скоро канет в /dev/null, я решил кое-какие свои заметки перенести сюда.

Автоподключение модема huawei при помощи udev



Если вы являетесь "счастливым обладателем" МТС-модема, то прежде всего вам понадобится установить пакеты wvdial и usb_modeswitch. Первый нужен для поднятия ppp-соединения, а второй — для переключения этого "свистка" из режима эмуляции CD-ROM в режим модема.

Затем нужно при помощи lsusb после подключения модема узнать его VID и PID (они нужны будут для usb_modeswitch).

После того, как это готово, создаем такое правило udev:
SUBSYSTEM=="usb", ACTION=="add", ATTRS{idProduct}=="1436", ATTRS{idVendor}=="12d1", MODE:="0666", RUN+="/etc/udev/rules.d/huawei on"
SUBSYSTEM=="usb", ACTION=="remove", ATTRS{idProduct}=="1436", ATTRS{idVendor}=="12d1", RUN+="/etc/udev/rules.d/huawei off"

И создаем исполняемый файл /etc/udev/rules.d/huawei с содержимым
#!/bin/bash
STATUS=0

if [ "$1" = "on" ]; then
        rc.d stop network
        sleep 1
        aplay /usr/share/skype/sounds/CallConnecting.wav
        usb_modeswitch -v 0x12d1 -p 0x1436 -H
        echo "AT^SYSCFG=14,2,3fffffff,0,1" > /dev/ttyUSB2
        while [ $STATUS = 0 ]; do
                aplay /usr/share/skype/sounds/CallConnecting.wav
                sleep 1
                wvdial &
                PID=$!
                sleep 1
                ps $PID && STATUS=1 || aplay /usr/share/skype/sounds/CallFailed.wav
        done
        aplay /usr/share/skype/sounds/CallRingingIn.wav
else
        killall -9 wvdial
        killall -9 pppd
        rc.d start network

fi

(Если у вас не установлен skype, измените звуки на какие-нибудь другие).

Все, теперь при подключении модема будут проигрываться характерные звуки и подниматься ppp-соединение, а при отключении — соединение будет разрываться.

P.S. Для МТС-модема нужен еще файл настройки wvdial (/etc/wvdial.conf):
[Dialer Defaults]
Init1 = ATZ
Init2 = AT+CGDCONT=1, "IP", "internet.mts.ru"
Modem Type = USB Modem
Modem = /dev/ttyUSB0
Baud = 57600
New PPPD = yes
Phone = *99***1#
Password = mts
Username = mts
Stupid Mode = yes


Звук по сети



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

Итак, нужно нам для этого немного: лишь активировать звуковое устройство-петлю snd-aloop (модуль — часть Alsa), а с удаленной машины (со звуком) зайти на нашу (без звука) и запустить передачу звука по сети при помощи sox (посредством этого устройства-петли) и воспроизведение его локально (через трубу).

Теперь подробности. Назовем машину, не имеющую акустической системы, speakerless, а имеющую — speakerfull.

  1. На машине speakerless запускаем звуковое устройство-петлю:
    modprobe snd-aloop index=0 pcm_substreams=1
    

    Это устройство создает две виртуальные звуковые карты: вход одной перенаправляется на выход другой.
  2. В файл .asoundrc пишем следующее:
    pcm.!default {
      type dmix
      slave.pcm "hw:Loopback,0,0"
    }
    pcm.loop {
      type plug
      slave.pcm "hw:Loopback,1,0"
    }
    

    Таким образом по умолчанию звук от всех приложений микшируется и направляется на вход виртуальной звуковой карты номер 0. Забрать звук мы сможем с выхода виртуальной звуковой карты номер 1, которую мы обозвали синонимом loop.
  3. На машине speakerfull делаем следующее:
    ssh -C speakerless sox -q -t alsa loop -t wav -b 24 -r 48k - | play -q -
    

    Т.е. мы соединяемся по ssh с машиной speakerless, запускаем там sox для воспроизведения звука с устройства loop (т.е. нашей виртуальной звуковой карты номер 1), отправляем его в трубу и воспроизводим уже на машине speakerfull при помощи play.
  4. Все. Наслаждаемся звуком.

P.S. Добавлю отсебятины: по идее, можно поменять местами правую и левую части в команде ssh (соответственно, заменив speakerless на speakerfull). Тогда мы сможем держать машину speakerfull постоянно включенной, а ssh-подключение активировать при загрузке машины speakerless (естественно, сначала настроив авторизацию по ключам).

P.P.S. Еще (теоретически) можно реализовать это без ssh, открыв на машине speakerfull определенный порт (слушать его можно, например, curl'ом), данные из которого перенаправлять сразу на play. Тогда машине speakerless "выхлоп" sox'а надо будет послать (опять-таки, например, через curl) на нужный порт машины speakerfull.

Локализация



Существует множество способов локализации своих приложений. Наиболее распространенным является использование gettext, но бывают случаи, когда необходим более простой способ локализации, когда перевод "вшит" в бинарник (и когда языков немного).

Статическая локализация


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

Определим макросы _LANG и _L, которые позволяют выбрать нужный текст в зависимости от текущего языка. Язык определяется переменной Lang:
#define		_LANG(_var, _ru, _en)	char _var##ru[] = _ru;\
					char _var##en[] = _en;\
					char *_var[2] = {_var##ru,  _var##en};
#define		_L(x)	(x[Lang])
unsigned char Lang = 1; // по умолчанию - английский

Макрос _LANG инициализирует очередной массив с именем _var двумя строками - следующими аргументами. Чтобы вывести нужный текст, используется макрос _L, который просто выводит нужную строку из массива, в зависимости от языка. Вот пример использования этого метода:
char *ptr = getenv("HTTP_ACCEPT_LANGUAGE");
if(ptr && strncmp(ptr, "ru", 2) == 0) Lang = 0;
...
printf("%s:<input ... >\n", _L(_s_Name_));

Отдельно необходимо будет заполнить массив с фразами на русском и английском языке:
_LANG(_s_Name_, "Ваше имя", "Your name");

Достоинства этого метода: в одном бинарнике содержится все, т.е. меньше шансов при переносе CGI из одного хранилища в другое "потерять" локализацию. Минусы тоже очевидны: приходится отдельно подготавливать строковые массивы, а в месте использования правильно указать название массива. Еще один минус: для добавления еще одного языка придется править исходники и перекомпилировать приложение.

Динамическая локализация


Gettext - стандартный способ локализации. В отличие от статической локализации он требует наличия еще как минимум одного дополнительного файла (mo-файл), в котором содержится база данных локализации для функции gettext. Т.к. локализация лежит отдельно, ее всегда можно подправить, не влезая в нутро исходников программы. Однако, при работе с gettext'ом выбор нужной фразы производится в реальном времени: для отображения фразы в соответствии с настройками локали, в базе данных для текущей локали ищется английская фраза (запакованная в макрос gettext). Если есть фраза на нужном языке, она выводится gettext'ом, иначе - отображается фраза на английском.

Средства работы с gettext автоматизированы: утилита xgettext выбирает из исходников нужные фразы (оформленные определенным макросом) и помещает их в po-файл, а утилита poedit позволяет редактировать po-файлы (кроме того, их можно редактировать вручную).

База данных (mo-файл) для каждой локали формируется при помощи утилиты msgfmt из текстового po-файла (который, кстати, тоже отдельный для каждой локали). Так как в процессе разработки зачастую приходится к уже имеющемуся po-файлу с переводом добавлять новые фразы, есть удобная утилита msgmerge, добавляющая лишь новые, не переведенные, фразы. Естественно, в cmake есть макросы для работы с gettext, так что разработчкику все сделать достаточно просто.

Чтобы было удобно работать с xgettext, необходимо определить какие-то простые макросы для gettext, например:
#define _(String)				gettext(String)
#define gettext_noop(String)	String
#define N_(String)				gettext_noop(String)

В этом случае, например, фраза printf(_("Capture frame %d\n"), j); при запуске xgettext с обозначением разделителей как -k_ -kN_ создаст po-файл, в котором будут строки
msgid   "Capture frame %d\n"
msgstr  ""

msgid - идентификатор нашего сообщения (т.е. само сообщение на английском), а msgstr - это же сообщение на другом языке.

Подготовив такой файл, переведя его и сформировав из него при помощи msgfmt базу данных для нашей локали, мы сможем видеть перевод. Но для этого еще необходимо будет указать gettext'у, где искать mo-файл. Это делается при помощи функций
bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);
textdomain(GETTEXT_PACKAGE);

Макросы GETTEXT_PACKAGE и LOCALEDIR определяются обычно при подготовке компиляции (например, тем же cmake). LOCALEDIR - путь к директории, в которой лежат файлы локализации (если мы хотим русифицировать приложение, в директории LOCALEDIR должна быть вложена директория ru/LC_MESSAGES, где ru - нужный нам язык, LC_MESSAGES - сами сообщения (по значению этой переменной и определяется язык, кстати).

Чтобы оформить все это в CMakeLists.txt, достаточно указать, что нам необходимо подключить нужные библиотеки для работы gettext и найти xgettext (если po-файл еще не сформирован):
find_package(Gettext REQUIRED)
find_program(GETTEXT_XGETTEXT_EXECUTABLE xgettext)

Введем макросы:
  • PO_FILE - po-файл, формируемый xgettext;

  • RU_FILE - переведенный po-файл (в который будут добавляться новые данные при помощи msgmerge);

  • MO_FILE - mo-файл (т.е. сама база данных - только он и нужен в конечном итоге).

Тогда для генерирования po- и mo-файлов "на лету" нам нужно дописать в CMakeLists.txt:
add_custom_command(
	OUTPUT ${PO_FILE}
	COMMAND ${GETTEXT_XGETTEXT_EXECUTABLE} --from-code=koi8-r ${SOURCES} -c -k_ -kN_ -o ${PO_FILE}
	COMMAND sed 's/charset=UTF-8/charset=koi8-r/' ${PO_FILE} | enconv > tmp && mv -f tmp ${PO_FILE}
	COMMAND ${GETTEXT_MSGMERGE_EXECUTABLE} -Uis ${RU_FILE} ${PO_FILE}
	DEPENDS ${SOURCES})
add_custom_command(
	OUTPUT ${MO_FILE}
	COMMAND ${GETTEXT_MSGFMT_EXECUTABLE} ${RU_FILE} -o ${MO_FILE}
	DEPENDS ${RU_FILE})

(локаль у меня - КОИ8, поэтому необходимо указать входную локаль для xgettext'а, а также подкорректировать неправильное отображение локали в формируемом po-файле.
Облегчение работы переводчика
Ключ -c позволяет в po-файл добавлять строку с комментариями, расположенными до строки, где встречается макрос, указанный в аргументе ключа -k. Таким образом, если в комментариях сразу писать перевод на русский язык, можно даже будет автоматизировать создание готового po-файла с переводом.

Если все выполнить правильно и не забыть скопировать mo-файл по указанному пути, то после запуска приложение должно "заговорить" на родном для вашей локали языке.

Монтирование разделов на сменных носителях при помощи udev



Существует множество способов монтирования сменных носителей. Один из них - монтирование при помощи udev.

Редактируя правила udev, можно сделать один из двух вариантов монтирования: вручную (здесь про него и будет рассказано) или автоматически. Вариант монтирования автоматически я не буду описывать, т.к. он неудобен тем, что требует для отмонтирования либо прав root'а; либо настройки sudo; либо приходится извлекать носитель, не отмонтировав его, что может быть чревато. Да и не всегда нужно монтировать подключенный сменный носитель (например, если на него нужно записать образ при помощи dd, отформатировать его и т.п.).

Описанный способ использует udev для того, чтобы при подключении сменного носителя создать директорию в /media и соответствующую запись в /etc/fstab. Для удобного распознавания подключенного носителя имя директории в /media состоит из названия файловой системы раздела и имени раздела в /dev.

Итак, создаем файл /etc/udev/rules.d/mnt.rules и заносим в него следующее:
KERNEL=="sd[a-z]", GOTO="do-disk-rules"
KERNEL!="sd[a-z][0-9]", GOTO="end-of-file"
LABEL="do-disk-rules"
KERNEL=="sd[a-z]", GROUP="users"
ACTION=="remove", ENV{ID_FS_TYPE}!="", RUN+="/bin/sed -i '/\/dev\/%k /d' /etc/fstab"
ACTION=="remove", ENV{ID_FS_TYPE}!="", RUN+="/bin/rmdir /media/$env{ID_FS_TYPE}-%k"
ACTION=="add", ENV{ID_FS_TYPE}!="", RUN+="/bin/mkdir -p /media/$env{ID_FS_TYPE}-%k"
# монтирование раздела fat32
ACTION=="add", ENV{ID_FS_TYPE}=="vfat", RUN+="/bin/sed -i '$a\/dev/%k /media/$env{ID_FS_TYPE}-%k vfat rw,noauto,noatime,dmask=022,gid=user,user,fmask=133,iocharset=koi8-r 0 0' /etc/fstab", OPTIONS="last_rule"
# монтирование раздела ntfs
ACTION=="add", ENV{ID_FS_TYPE}=="ntfs", RUN+="/bin/sed -i '$a\/dev/%k /media/$env{ID_FS_TYPE}-%k ntfs-3g rw,noauto,dmask=022,fmask=133,gid=user,user,iocharset=koi8-r 0 0' /etc/fstab", OPTIONS="last_rule"
# монтирование прочих ФС
ACTION=="add", ENV{ID_FS_TYPE}!="", RUN+="/bin/sed -i '$a\/dev/%k /media/$env{ID_FS_TYPE}-%k $env{ID_FS_TYPE}  defaults,user 0 0' /etc/fstab"
LABEL="end-of-file"

Эти правила будут работать в дистрибутивах с относительно старыми udev. Для работы с более свежими версиями udev (например, 175-1) придется немного изменить правила:
# монтирование съемных накопителей
KERNEL=="sd[a-z]", GOTO="do-disk-rules"
KERNEL!="sd[a-z][0-9]", GOTO="end-of-file"
LABEL="do-disk-rules"
ACTION=="add", ENV{ID_USB_DRIVER}="usb-storage", GROUP="storage"
IMPORT{program}="/sbin/blkid -o udev -p %N"
ACTION=="remove", ENV{ID_FS_TYPE}!="", RUN+="/bin/sed -i '/\/dev\/%k /d' /etc/fstab"
ACTION=="remove", ENV{ID_FS_TYPE}!="", RUN+="/bin/rmdir /media/$env{ID_FS_TYPE}-%k"
ACTION=="add", ENV{ID_FS_TYPE}!="", RUN+="/bin/mkdir -p /media/$env{ID_FS_TYPE}-%k"
# монтирование раздела fat32
ACTION=="add", ENV{ID_FS_TYPE}=="vfat", RUN+="/bin/sed -i '$a\/dev/%k /media/$env{ID_FS_TYPE}-%k vfat rw,noauto,noatime,dmask=022,user,fmask=133,iocharset=koi8-r 0 0' /etc/fstab"
# монтирование раздела ntfs
ACTION=="add", ENV{ID_FS_TYPE}=="ntfs", RUN+="/bin/sed -i '$a\/dev/%k /media/$env{ID_FS_TYPE}-%k ntfs-3g rw,noauto,dmask=000,fmask=111,user,locale=ru_RU.koi8-r,allow_other 0 0' /etc/fstab"
# монтирование прочих ФС
ACTION=="add", ENV{ID_FS_TYPE}!="", ENV{ID_FS_TYPE}!="ntfs|vfat", RUN+="/bin/sed -i '$a\/dev/%k /media/$env{ID_FS_TYPE}-%k $env{ID_FS_TYPE}  defaults,user 0 0' /etc/fstab"
LABEL="end-of-file"

Естественно, если у вас юникод, опцию locale указывать не надо. Если кодировка другая - измените koi8-r на свою кодировку.

P.S. В новых версиях ntfs-3g "поломали" опцию user, поэтому для того, чтобы пользователь мог монтировать флешки с ntfs, необходимо либо скомпилировать ntfs-3g с поддержкой опции user, либо настроить sudo. Для настройки sudo достаточно вписать в /etc/sudoers следующее:
ALL ALL=(ALL) NOPASSWD: /bin/ntfs-3g,/bin/mount,/bin/umount


Клавиатура Microsoft NEK 4000



Из коробки" пока, к сожалению, на этой клавиатуре движок "Zoom" не работает. Однако, его можно настроить. Так как настройка делается при помощи xdotool, можно генерировать zoom'ом любые клавиатурные или "мышиные" последовательности. Итак, краткий алгоритм настройки:

  1. Создаем правило keymap (файл /lib/udev/keymaps/microsoft-ergonomic-keyboard) для назначения скан-кодов нераспознаваемым сигналам от zoom:
    0xC022D 0xC1 # Zoom Up which we wish to be Scroll up
    0xC022E 0xC2 # Zoom Down which we wish to be Scroll down
    


  2. Создаем файл /etc/udev/rules.d/ms_ergo.rules для автозапуска keymap с содержимым
    SUBSYSTEM=="input", ATTRS{manufacturer}=="Microsoft", RUN+="keymap $name microsoft-ergonomic-keyboard"
    


  3. Назначаем на новые скан-коды сигналы колеса мыши. Для этого создаем (если не было) файл ~/.xbindkeysrc и заносим в него
    "xdotool click 4"   # Scroll Up
       c:201
    "xdotool click 5"   # Scroll Down
       c:202
    


  4. В автозапуск своего DE/WM прописываем xbindkeys. Например, в IceWM для этого нужно добавить в файл ~/.icewm/startup строчку
    xbindkeys &  # чтобы zoom на клавиатуре работал
    


Естественно, чтобы все работало, нужно установить xdotool и xbindkeys.

После всех вышеперечисленных телодвижений "zoom" начинает работать как прокрутка колесом мыши.

Инициализация генератора псевдослучайных чисел



Инициализация генератора псевдослучайных чисел зачастую является наболевшим вопросом. Приведу код, который содержит в себе два способа инициализации.
#include <stdio.h>		// printf
#include <stdlib.h>		// random number functions
#include <sys/time.h>		// gettimeofday
#include <limits.h>		// LONG_MAX
#include <math.h>		// floor
#include <unistd.h>		// read
#include <sys/types.h>		// open
#include <sys/stat.h>		// open
#include <fcntl.h>		// open

double dtime(){
	double t;
	struct timeval tv;
	gettimeofday(&tv, NULL);
	t = tv.tv_sec + ((double)tv.tv_usec)/1e6;
	return t;
}

void urandom_ini(){
	double tt = dtime() * 1e6;
	double mx = (double)LONG_MAX;
	srand48((long)(tt - mx * floor(tt/mx)));
}

void dev_random_ini(){
	long r_ini;
	int fd = open("/dev/random", O_RDONLY);
	if(-1 == fd){fprintf(stderr,"Error open /dev/random!\n"); urandom_ini(); return;}
	if(sizeof(long) != read(fd, &r_ini, sizeof(long))){
		fprintf(stderr,"Error read /dev/random!\n"); urandom_ini();
		close(fd); return;
	}
	close(fd);
	srand48(r_ini);
}

int main(int argc, char **argv){
	int i;
	printf("\trandom\t\tdrand48\n");
	dev_random_ini();
	for(i = 0; i < 10; i++){
		printf("\t%ld\t%g\n", random(), drand48());
	}
	printf("\n");
	return 0;
}

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

Если этот инициализатор не срабатывает, запускается менее надежный инициализатор на основе значения текущего времени.
Tags: snippets, всячина
Subscribe

  • M$ teams…

    Начал с сегодняшнего дня студентам ЮФУ удаленно лекции читать. У них все завязано на различные корпорации зла. И базовая работа - через teams. ОК,…

  • Почему systemd — дерьмо

    Уже давно на эту статейку натыкался, но все забывал в "закладки" добавить. Вот, добавляю: "systemd — отстой". Советую эту статейку почитать…

  • Разбираемся с утечками

    Занялся полным рефакторингом кода системы управления оптоволоконным спектрографом. А глаз уже "замыленный": ну вот не могу сходу обнаружить, где я…

promo eddy_em september 3, 12:13 8
Buy for 10 tokens
Уже больше полугода занимаюсь разработкой, вот, наконец-то в мастерских взялись за меня и начали выдавать первые детали. Сегодня сделал тестовую сборку (как обычно, местами пришлось "доработать напильником"): Пока прибор без названия (да и как-то не лезет в голову ничего, у меня нет…
  • Post a new comment

    Error

    Anonymous comments are disabled in this journal

    default userpic
  • 3 comments