26 мая 2009 г.

Отключение Network Manager в Ubuntu 9.04

Почему?
  • PPTP плагин до сих пор не научился автоматически подключаться при входе в систему. Про автоматическое подключение на этапе загрузки системы я вообще молчу.
  • В некоторых случаях не запоминает пароль на подключение и его приходится каждый раз вводить.
  • Я пользуюсь сетевыми файловыми системами (cifs). Так вот, Network Manager при завершении работы системы отключает сетевые интерфейсы ДО отмонтирования сетевых файловых систем. Последние при этом подвисают на 60 секунд каждая.
  • Я не пользуюсь Wi-Fi и поэтому мне не нужно постоянно переподключаться между несколькими сетями.
  • Мои настройки сети статичны и меняются крайне редко.
В чем проблема?

Казалось бы, достаточно удалить пакет network-manager в синаптике и всё. Ан нет. Действительно, удаление названного пакета отключает NetworkManager. Однако конфигурация сети, заданная в традиционном файле /etc/network/interfaces при этом обрабатывается не совсем корректно. Вот мой файл конфигурации:
auto lo eth0 ppp0
iface lo inet loopback

iface eth0 inet static
address 192.168.1.33
netmask 255.255.0.0
broadcast 192.168.255.255
gateway 192.168.1.10


iface ppp0 inet ppp
provider rez
В нем определно 3 сетевых интерфейса: локальная петля (lo), физический Ethernet (eth0) и PPTP интерфейс, по которому я хожу в инет (ppp0).
Строка "auto lo eth0 ppp0" говорит системе о том, что нужно сначала поднять lo, затем eth0, и в последнюю очередь ppp0.
Что же происходит на деле? При загрузке системы действительно поднимается lo, затем eth0, а вот ppp0 поднимается... еще ДО старта syslogd! То есть еще даже до инициализации сетевой подсистемы Linux. Причем демон pppd запускается аж в трёх экземплярах! Разумеется, что подключиться он не может, соединение разрывается, он пробует снова и вся эта чехарда продолжается до бесконечности...
Раньше (в Ubuntu 8.04) такого поведения не было.

Как?

Как оказалось, виноват во всём udev. Ну разумеется он, потому что кто же еще мог запустить pppd на такой ранней стадии запуска системы? Начал смотреть правила в /lib/udev/rules.d/ и обнаружил там вот что: 85-ifupdown.rules:
# Bring devices up and down only if they're marked auto.
# Use start-stop-daemon so we don't wait on dhcp
SUBSYSTEM=="net", TEST!="/var/run/network/ifstate", RUN+="/bin/mkdir /var/run/network"
SUBSYSTEM=="net", ACTION=="add", RUN+="/sbin/start-stop-daemon --start --background --pidfile /var/run/network/bogus --startas /sbin/ifup -- --allow auto $env{INTERFACE}"
SUBSYSTEM=="net", ACTION=="remove", RUN+="/sbin/start-stop-daemon --start --background --pidfile /var/run/network/bogus --startas /sbin/ifdown -- --allow auto $env{INTERFACE}"
Udev, как только находил новый сетевой интерфейс в системе, запускал скрипт /sbin/ifup, который в свою очередь пытался поднять интерфейс pppd раньше времени.
Прочитав комментарий в /lib/udev/rules.d/README, я скопировал файл 85-ifupdown.rules в папку /etc/udev/rules.d и привел его к такому виду:
# Bring devices up and down only if they're marked auto.
# Use start-stop-daemon so we don't wait on dhcp
SUBSYSTEM=="net", TEST!="/var/run/network/ifstate", RUN+="/bin/mkdir /var/run/network"
SUBSYSTEM=="net", ACTION=="add"
SUBSYSTEM=="net", ACTION=="remove"
То есть по сути отключил действия, выполняющиеся при подключении/отключении сетевых адаптеров. Очевидно, это может как-то повлиять на работу систему, если вы в процессе её работы часто меняете сетевые адаптеры. Но в обычных условиях это правило практически бесполезно.

Теперь моя сеть инициализируется как положено и на этапе входа в систему я уже имею подключенный интернет.
Еще раз, что нужно сделать, чтобы в Ubuntu 9.04 отключить Network Manager:
1) Удалить пакет network-manager и, если хотите, относящиеся к нему.
2) Скопировать файл /lib/udev/rules.d/85-ifupdown.rules в /etc/udev/rules.d/ и убрать в нем параметр RUN как указано выше.

21 мая 2009 г.

Включение и отключение демонов в Ubuntu

Все время забываю, как называется утилита для удобного включения/выключения демонов (сервисов) из /etc/init.d в Ubuntu.

Теперь точно не забуду: sysv-rc-conf

25 февр. 2009 г.

Инструмент "В контакте"

Начал новый проект: https://launchpad.net/vkontakte-tool

Для ленивых ходить по ссылкам, это - аналог программы Vkonpic, но работающий в Linux. Позволяет загружать на стены в контакте фотки вместо графити.
Написан на Python, то есть является кроссплатформенным.

На данный момент доступны исходный код и deb-пакет для Ubuntu.
Попозже будет доступна сборка для Windows.

11 февр. 2009 г.

Использование ссылок при обработке исключений C++

Просто предупреждаю.

Не попадитесь:
class AException {
public:
const int& x;
AException(const int& _x)
: x(_x)
{
}
}

class A {
private:
int* x;

public:
A()
{
x = new int;
*x = 2;
}
~A()
{
*x = 1;
delete x;
x = NULL;
}

void throw_func()
{
throw AException(*x);
}
};

void a_runner()
{
A a;
a.throw_func();
}

int main()
{
try
{
a_runner();
}
catch(AException& e)
{
cout << "Cought exception with x = " << e.x << endl;
}
}
Мораль сей басни такова. Когда вы в обработчике исключения попытаетесь обратиться к e.x, он уже будет некорректный, так как экземпляр объекта A будет уничтожен (вызовется деструктор) ДО того, как управление передается обработчику исключения.

6 февр. 2009 г.

Нюансы множественного наследования в C++

Недавно открыл для себя виртуальное наследование в C++.

Такой способ наследования тесно связан с другой возможностью C++ - множественным наследованием. Я бы даже сказал, он вполне логично дополняет один из способов применения множественного наследования.

Рассмотрим такой пример:

class A {
public:
int var_a;
virtual void foo() = 0;
};

class B: public A {
public:
int var_b;
void bar()
{
foo();
}
};

class C: public A {
public:
int var_c;
void foo()
{
cout << "This is C::foo()" << endl;
}
};

class D: public B, public C {
int var_d;
};
Все дальнейшие манипуляции с классами будем проводить в main().
Например, объявим экземпляр класса D и попробуем присвоить что-нибудь переменной var_a:
D d;
d.var_a = 2;
Компилятор не пропустит такое выражение и вот почему. Дело в том, что при использовании обычного невиртуального наследования экземпляр объекта D имеет следующую логическую структуру:

Как видно из схемы, мы имеем две переменных var_a, одна их которых принадлежит классу A, который является базовым классом для B, а другая принадлежит классу A, который является базовым классом для C.
Для обхода этой ситуации можно конечно явно указать, какую переменную мы имеем ввиду:
d.B::A::var_a = 2;
или:
d.C::A::var_a = 2;
Но это, согласитесь, не очень красиво, да и не всегда логически необходимо иметь два экземпляра базового класса.
Вот именно в этом случае к нам приходит на помощь виртуальное наследование.
Есть и еще один нюанс в нашем примере: взгляните на функцию B::bar(). В ней происходит вызов виртуальной функции A::foo(), которая для этой ветви иерархии неопределена - ни в классе A, ни в классе B, ни в классе D. К классу C функция B::bar() доступа не имеет, так как C по сути наследует от другого экземпляра A.

Что же будет, если применить виртуальное наследование? Слегка изменим наш пример:
class A {
public:
int var_a;

virtual void foo() = 0;
};


class B: public virtual A {
public:
int var_b;

void bar()
{
foo();
}
};


class C: public virtual A {
public:
int var_c;
void foo()

{
cout << "This is C::foo()" << endl;
}
};

class D: public B, public C {
int var_d;
};

Теперь экземпляр класса D будет иметь другую логическую структуру:

Как видно из рисунка теперь для классов B и C создается единственный общий экземпляр класса A.
Таким образом код
D d;
d.var_a = 2;

становится допустимым.

При применении виртуального наследования становится возможном и еще один интересный финт ушами - использование виртуальной функции, фактически определенной не в родительском и не в дочернем классах, а в классе-брате. В нашем примере - это виртуальная функция A::foo(), которая определена в классе C - C::foo(). Посмотрите, она используется из функции B::bar(), который является братом класса C.
Естественно, чтобы это работало, мы должны создать экземпляр такого объекта, который бы наследовал от обоих классов B и C, в нашем случае - это класс D. Таким образом, становится допустимым код:
D d;
B& b = d; //создали ссылку типа B на объект типа D.
b.bar(); //вызываем функцию bar(), которая в свою очередь вызывает функцию foo(), которая фактически определена в классе C.

Программиста, который получил в свой метод ссылку типа B, вообще может ввести в заблуждение работоспособность подобного кода, ведь он не знает о том, что на самом деле эта ссылка указывает на объект типа D (он вовсе не видит объявления класса D), который наследует и от C тоже. С его точки зрения это будет казаться мистикой - ведь функция bar() вызывает виртуальную функцию foo(), которая с его точки зрения НИГДЕ не определена!

Ну и напоследок еще одна задачка, связанная с множественным наследованием:

class B
{
public:
int var_b;
};

class C
{
public:
int var_c;
};

class D: public B, public C
{
public:
int var_d;
};

using namespace std;

int main()
{
D* d = new D;

B* b = d;
C* c = d;

void* v_d = d;
void* v_b = b;
void* v_c = c;

cout << (b == d) << endl;
cout << (c == d) << endl;

cout << endl;

cout << (v_b == v_d) << endl;
cout << (v_c == v_d) << endl;

return 0;
}
Программа выведет:
1
1
1
0

Внимание вопрос: почему в последнем случае получается ноль (false)? :)

29 дек. 2008 г.

ALSA и FFmpeg

На днях задался целью дописать поддержку захвата звука к одному свободному приложению (если быть точным, то это Motion).

С инструментарием определился быстро:
Для захвата звука - ALSA, так как моя звуковая карта работает именно с ней.
Для сжатия и сохранения в файл - FFmpeg (его библиотеки avcodec и avformat), так как именно ими пользуется Motion для сохранения видео.

С ALSA все более-менее понятно, есть немного внятная документация и автоматически сгенерированное описание API там же. Также я смотрел исходники программы aplay и arecord (которые на самом деле - одна и та же программа).
А вот с FFmpeg всё оказалось сложнее - никакой внятной документации для него нет, даже на английском языке. Есть автоматически сгенерированное описание API и несколько примеров:
  1. В самом проекте - показано, как использовать avcodec на примере кодирования/декодирования аудио и видео отдельно друг от друга. При этом работа с медиафайлами реализована напрямую без использования avformat.
  2. Пример от Martin Böhme рассказывает о том, как с помощью avformat и avcodec открыть и декодировать видеофайл. Он не касается звука, а также кодирования аудио/видео информации.
  3. Пример от dranger являет собой урок по созданию с нуля простенького видеоплеера, который для чтения и декодирования файлов использует avformat и avcodec, а для воспроизведения видео и звука, а также для взаимодействия с клавиатурой использует библиотеку SDL. Здесь тоже не затрагиваются вопросы кодирования.
Поскольку я до этого никогда не программировал с использованием ALSA и FFmpeg, то, прежде чем что-то внедрять в Motion, я решил написать простенькую програмку для захвата звука со звуковой карты и сжатия его в формат ogg.

Вот что у меня получилось: http://ls-it.googlecode.com/svn/trunk/alsa-capture/
Для сборки просто введите make. После запуска программа попытается записать несколько секунд звука с ALSA-устройства default в файл output.ogg, который будет создан в папке с программой.

Я постарался снабдить исходный код большим количеством комментариев на русском языке, чтобы было легче разобраться в нём. Ваши комментарии, доработки и предложения всячески приветсвуются. :)

24 дек. 2008 г.

Открытие

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

Поэтому я решился на создание блога, да не просто блога - а серии блогов.

В блоге "L-S: IT" я планирую размещать свои мнения и предложения относительно различных проявлений такой области как Информационные Технологии. :)