cppmm's Tech blog

Разные, на мой взгляд, интересные замечания, связанные с компьютерами в частности и техникой вообще.

вторник, 21 февраля 2012 г.

Восстановление базы установленных пакетов в debian

Решил тут перейти на нетбуке со стабильной ветки debian'а на тестинг. Так как на моей eeepc'шке всего 4 гига флешка внутри, места для dist-upgrade не хватило бы. Но я уже обновлял его с lenny до squeeze в своё время, так что это не проблема. Беру обычную флешку, форматирую в ext3, подключаю и тут я допускаю первую ошибку. Вместо того, чтобы просто примонтировать эту флешку как /var/cache/apt/archives, я копирую на неё содержимое /var/ и командую mount /dev/sdb1 /var/. Само по себе это тоже не проблема. Я спокойненько обновился, отмонтировал флешку и ребутнулся. Это было второй ошибкой. Дело в том, что помимо всего прочего на /var/ лежит база установленных пакетов dpkg. Находится она в /var/lib/dpkg/status. И тут я понимаю, что после того, как система обновилась, я допустил третью ошибку. Мне понадобилась флешка и я её снова форматнул.
Итак, ситуация. На нетбуке установлен Debian Wheezy. Но dpkg думает, что там squeeze(я же монтировал просто поверх /var/, и всё, что там было, осталось неизменным). В итоге dpkg и apt в ступоре. Они смотрят в базы установленных пакетов, сравнивают их с репами и понимают, что надо обновляться. Но при попытке что-нибудь установить или обновить, dpkg проверяет установленные файлы и понимает, что они не соответствуют тому, что у него в базе(версии-то разные) и отказывается что-либо делать. И я его понимаю. Например, он ругается на libc6. Он же видит, что у меня динамический линковщик ссылается на определённую библиотеку. Но у него в базе говорится, что стоит другая библиотека. Поэтому dpkg честно выдаёт "Ты там разберись сперва с тем, что в обход меня поставил, а потом уже обновляйся, а то я тебе всё сломать могу". Итого - система как бы работает, но пакетный менеджер как бы умер.
Есть два выхода. Каким-то образом восстановить dpkg или переустановить всё с нуля, забекапив конфиги. В случае со вторым вариантом, желательно ещё узнать, какие пакет у меня были установлены, чтобы потом не сидеть и не вспоминать, чего там где было. И пока я думал, как же вытащить список пакетов, в голову пришла идея "если у меня будет список пакетов, зачем переустанавливать? Можно же просто записать их в базу dpkg и работать дальше". На том и порешили.
Для начала надо получить список. Чем славится debian(ну и любой нормальный linux)? Правильно. Тем, что у него на каждый пакет обязательно существует документация. И находится она прямо в системе. Не нужно ни интернетов, ни толстых книжек. Всё с собой. А это значит, что мне нужно посмотреть, на что у меня есть доки и я узнаю, какие у меня стоят пакеты. Решение для первой части проблемы есть.
Но это даст мне только названия пакетов. А для dpkg нужно версии, зависимости и ещё целую кучу всякой информации. И лучшее место, где взять эту информацию - репы. Собственно, это всё тоже уже в системе. Мы получаем все версии, описания, зависимости и т.д., когда командуем apt-get update. И, к счастью, для этой команды не важно - есть там что-то в базе установленного или нет. Она работает и так.
Осталось дело за малым - написать скрипт, который пройдётся по докам, соберёт имена, а потом с этими именами пробежится по спискам пакетов из реп, распарсит это дело и сложит в нужном виде в базу dpkg.

Собственно, вот этот скрипт.

#!/usr/bin/perl -w
#

my $docsdir = "usr/share/doc/";
my $listsdir = "var/lib/apt/lists/";

my (@packages, @lists);

# Get package names
print "Reading all installed packages.\n";

opendir(DOCSDIR, "$docsdir") or die "Couldn't open $docsdir : $!";
while (defined(my $packagename = readdir(DOCSDIR))) {
        push(@packages, $packagename) if (!($packagename =~ m/([A-Z]|^debian$|\.+)/));
        print ".";
}
print "\n";
closedir(DOCSDIR);
print "Reading complete.\n";
my $p_count = $#packages + 1;
print "There was $p_count packages found.\n";
undef $p_count;

# Get package indexes
opendir(LISTSDIR, "$listsdir") or die "Couldn't open $listsdir : $!";
while (defined(my $listname = readdir(LISTSDIR))) {
        push(@lists, $listname) if ($listname =~ m/Packages/);
}
closedir(LISTSDIR);

# Searching information and make new dpkg status file.
open(my $statusfile, '>', 'status');
foreach my $package (@packages) {
        my %information;
        foreach my $list (@lists) {
                my $filepath = join('/', $listsdir, $list);
                open(my $listfile, '<', $filepath);
                my $write = 0;
                my $multiline = 0;
                while ( defined($line = readline($listfile)) ) {
                        if (grep(/^Package: $package$/, $line)) {
                                print "Package $package found in list $list.\n";
                                $write = 1;
                        }
                        if ( ($write) && (!$multiline) ) {
                                if ( $line =~ m/^Tag/ ) {
                                        $multiline = 1;
                                } else {
                                        my @data = split(/:\s/, $line);
                                        $information{"$data[0]"} = $data[1];
                                }
                        }
                        if ( ($write) && ($multiline) ) {
                                if ( $line =~ m/^Tag/ ) {
                                        my @data = split(/:\s/, $line);
                                        $information{"$data[0]"} = $data[1];
                                } elsif ( $line =~ m/^\s/ ) {

                                        $information{"Tag"} = join('', $information{"Tag"}, $line);
                                } else {

                                        $multiple = 0;
                                }
                        }
                        $write = 0 if ($line =~ m/^$/);
                }
                close($listfile);
        }
        print { $statusfile } "Package: $information{'Package'}" if (defined($information{"Package"}));
        print { $statusfile } "Status: install ok installed\n" if (defined($information{"Package"}));
        print { $statusfile } "Priority: $information{'Priority'}" if (defined($information{"Priority"}));
        print { $statusfile } "Section: $information{'Section'}" if (defined($information{"Section"}));
        print { $statusfile } "Installed-Size: $information{'Installed-Size'}" if (defined($information{"Installed-Size"}));
        print { $statusfile } "Maintainer: $information{'Maintainer'}" if (defined($information{"Maintainer"}));
        print { $statusfile } "Architecture: $information{'Architecture'}" if (defined($information{"Architecture"}));
        print { $statusfile } "Source: $information{'Source'}" if (defined($information{"Source"}));
        print { $statusfile } "Version: $information{'Version'}" if (defined($information{"Version"}));
        print { $statusfile } "Depends: $information{'Depends'}" if (defined($information{"Depends"}));
        print { $statusfile } "Description-md5: $information{'Description-md5'}" if (defined($information{"Description-md5"}));
        print { $statusfile } "Homepage: $information{'Homepage'}" if (defined($information{"Homepage"}));
        print { $statusfile } "Multi-Arch: $information{'Multi-Arch'}" if (defined($information{"Multi-Arch"}));
        print { $statusfile } "\n" if (defined($information{"Package"}));
}
close($statusfile);

syntax highlighted by Code2HTML, v. 0.9.1

Небольшие пояснения. Сначала мы указываем пути, где лежит документация и где списки пакетов, скачанные с реп при обновлении. Потом получаем список пакетов и начинаем по очереди для каждого из них искать информацию, складывать её в хеш, из которого потом в свою очередь записывать в файл в формате, необходимом dpkg. Вот и всё. Запускается скрипт без параметров, работает долго, на выходе выдаёт файл status, который надо положить в /var/lib/dpkg/.

Какие можно из всего этого сделать выводы?
1. Не начинайте делать серьёзных вещей в два-три часа ночи. По невнимательности можно допустить несколько казалось бы мелких ошибки, из-за которых потом придётся долго отдуваться.
2. GNU/Linux - система неубиваемая. Даже после конца света, она будет работать, хотя, может быть, людей уже и не останется.

суббота, 26 февраля 2011 г.

Ошибка Drupal и ImageCache при использовании nginx в качестве проксирующего web-сервера

Есть у меня под присмотром один сервер. Сервер это находится чёрт знает где, а компания, которая предоставляет его довольно жадная. На сервере около 20-ти разнообразных сайтов и на всё про всё 192 метра оперативки. Вот, недавно нагрузка окончательно стала добивать его, поэтому было решено воткнуть nginx как фронтенд как установленному там апачу(изначально не сделал так, потому как там не полноценный сервер, где что хочу, то и творю, а выделенный в chroot на FreeBSD). Собрал, поставил, настроил. Всё хорошо, нагрузка упала, но через некоторое время на части сайтов(те, что с Drupal, как раз) не отображались уменьшенные изображения залитых после перехода на связку nginx+apache картинок.

суббота, 15 января 2011 г.

Управление зонами в BIND9

Давно обещал немного рассказать про организацию своей домашней сети. Надеюсь, в дальнейшем это выльется в цикл статей.
Начнём с DNS.
У меня стоит отдельный роутер и разумеется внутри сети используются так называемые серые ip-адреса. Когда компов мало, можно ходить на них по ip, но я админ ленивый, а потому стараюсь всегда упростить себе жизнь. Ещё один минус связи исключительно по ip-адресам - скрипты. Ну, например, укажу я монтирование удалённого диска в скрипте по ip-адресу, а через пару месяцев захочется мне этот ip сменить(мало ли чего, всякое бывает). Придётся в скрипте править. А если таких скриптов много? Возникает проблема. Чтобы такого не было, лучше настроить связь между компами по именам, тогда при смене ip одного из компов, будет достаточно лишь поправить запись в DNS.

воскресенье, 26 декабря 2010 г.

Весёлый hddtemp.

ВНИМАНИЕ: Диск /dev/sdb не включен в базу данных поддерживаемых приводов.
ВНИМАНИЕ: Но с использованием распространенных параметров он что-то выдает.

Понимаю, что ничего необычного в этом выводе нет, но всё же как-то бросается в глаза фраза "он что-то выдаёт". :) Звучит настораживающе. 

вторник, 21 декабря 2010 г.

Украшаем процесс загрузки в Debian GNU/Linux

Вот тут для welinux.ru описал, как настроил у себя заставку grub2 и usplash на нетбуке под управлением Debian GNU/Linux.
А вот тут можно посмотреть, как оно выглядит.

суббота, 30 октября 2010 г.

Мои пакеты для debian.

Обзавёлся недавно доменом, поднял небольшой сервер и теперь балуюсь со всякими настройками. Пока что там особо ничего интересного нет, но один из планируемых разделов - собранные(или портированные) мной пакеты для debian. Пока что не оформлено в виде отдельного репозитария, в дальнейшем организую, с учётом архтектур и версий дистрибутива(заранее скажу, что поддерживать буду только stable и oldstable для архитектур i386 и x86_64). Пока доступно по адресу http://cppmm.net.ru/debian_packages/
Из планов на будущее, поднять небольшую вику с используемыми мной типичными конфигами и скриптами.
На данный момент скорость почти никакая - всё на стадии разработки, в дальнейшем ситуация улучшится.

четверг, 30 сентября 2010 г.

Автоматическое изменение яркости экрана Asus EeePC 701 4G в Debian

Я обычно работаю на своём нетбуке с яркостью, поднятой на максимум. Но при переходе в режим работы от батареи это даёт очень неслабую нагрузку на неё. Fn-клавиши, конечно, работают, но постоянно повышать/понижать яркость руками лень. Вот я и решил поковырять acpi на предмет автоматизации процесса. В принципе, ничего сложного.
Из man acpid можно узнать принципы работы acpi-скриптов. В /etc/acpi/events/ лежат файлы событий формата
event=событие
action=скрипт, который выполняется на это событие
Чтобы узнать, на какое именно событие реагировать, можно воспользоваться функцией acpi_listen.
В моём случае результат её работы выглядит так при выдёргивании блока питания из розетки:
[root@skynet ~]# acpi_listen
ac_adapter AC0 00000080 00000000
battery BAT0 00000080 00000001
hotkey ATKD 00000051 0000001e
battery BAT0 00000080 00000001
ac_adapter AC0 00000080 00000000
и вот так при подключении:
[root@skynet ~]# acpi_listen
ac_adapter AC0 00000080 00000001
battery BAT0 00000080 00000001
hotkey ATKD 00000050 0000001c
battery BAT0 00000080 00000001
ac_adapter AC0 00000080 00000001
Так как мне надо всего лишь сменить яркость, я не стал заморачиваться на параметры и указал реагировать просто на любые изменения в ac_adapter
Итоговый файл события у меня выглядит так:
[root@skynet ~]# cat /etc/acpi/events/auto_brightness
# /etc/acpi/events/auto_brightness
# Called when ac off and we need to switch brightness down or up when ac on.
#

event=ac_adapter.*
action=/etc/acpi/auto_brightness.sh
Теперь надо написать непосредственно скрипт, меняющий яркость взависимости от того, есть питание или нет. Здесь есть два пути - смотреть напрямую в /proc и парсить то, что там имеется или воспользоваться готовой функцией из acpi-support. Я выбрал второй вариант. Для тех, кому больше по душе первый, текущее положение адаптера отображается в /proc/acpi/ac_adapter/AC0/state

Я же использую функцию getState() из /usr/share/acpi-support/power-funcs. Она при вызове передаёт переменной STATE либо значение AC, либо BATTERY в зависимости от режима работы нетбука.
Осталось только найти способ менять яркость. Тут тоже всё просто. Пробежался быстро по тем же acpi'шным скриптам и выяснил, что уровень яркость указывается в /sys/class/backlight/eeepc/brightness. От 1 до 15, где 15 - максимально яркое, 1 соответсвенно тусклое.
Итоговый скрипт получился такой:
[root@skynet ~]# cat /etc/acpi/auto_brightness.sh
#!/bin/bash

. /usr/share/acpi-support/power-funcs

getState

if [ "$STATE" = "AC" ]
then
    echo 15 > /sys/class/backlight/eeepc/brightness
else
    echo 1 > /sys/class/backlight/eeepc/brightness
fi
Всё. Рестартим acpid и радумаемся.