10 авг. 2009 г.

Запуск программ на втором мониторе в Ubuntu 9.04

Приобрел недавно проектор для просмотра фильмов.

Подключил его вторым "монитором" к видеокарте (NVidia GeForce 7600 GT). После недолгих манипуляций с nvidia-settings удалось настроить проектор в режиме Separate X Screen (отдельный экран). Основной монитор и проектор заработали в родных разрешениях, а на проекторе появился пустой рабочий стол и две панельки от Gnome. Собственно это мне и нужно было.

Но вот незадача: все программы, запускаемые на втором мониторе через панели Gnome, открывались на первом мониторе! Единственным способом запустить что-либо на втором мониторе было использование комбинации alt+F2, что, согласитесь, не очень удобно.

Мучался я так пару дней. Потом надоело, я полез в свой любимый гугл и нашел вот это: http://bugzilla.gnome.org/show_bug.cgi?id=580103. Баг известен и пофикшен, но ждать пока он попадет в репозиторий долго. Поэтому пропатчил сам. Пакет для Ubuntu 9.04 amd64 лежит здесь: http://ls-it.googlecode.com/files/glib2.0_multimonitor.tar. Внутри архива 5 пакетов, их все нужно установить поверх имеющихся обычным способом.

Для тех, у кого не amd64, и для просто любопытных опишу процесс пропатчивания.
В первую очередь нужно добыть исходники glib2.0: apt-get source libglib2.0-0
Затем нужно установить зависимости, необходимые для сборки: sudo apt-get build-dep libglib2.0-0
Теперь нужно добыть патч: wget -O glibfix.patch http://bugzilla.gnome.org/attachment.cgi?id=135573
Патч нужно применить к файлу glib2.0-2.20.1/gio/gdesktopappinfo.c: patch -p1 < glibfix.patch
Необязательный шаг: поднять версию пакетов: debchange -i
Теперь можно собирать новые пакеты: cd glib2.0-2.20.1 && debuild
После успешного завершения в родительской папке вы должны получить набор пакетов, аналогичный моим, доступным по ссылке выше.

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)? :)