Архив за месяц: Декабрь 2013

Отладка Android-приложения на устройстве

Тут написано как отлаживать на устройстве под Android, точнее как сделать так что бы при запросе списка устройств мы видели не знаки вопросов, а верное имя.
Запросить список устройств можно так

1
~/adt-bundle-linux-x86-20131030/sdk/platform-tools/adb devices
~/adt-bundle-linux-x86-20131030/sdk/platform-tools/adb devices

и если мы видим что-то вроде

1
2
3
List of devices attached 
emulator-5554   device
????????????    no permissions
List of devices attached 
emulator-5554   device
????????????    no permissions

Нужно склонировать git clone https://code.google.com/p/51-android/ репозиторий с вендорами (я выбрал этот путь), а можно прописать только одно устройство, как написано в статье по ссылке
А как узнать что писать в idVendor и в idProduct ? Попробовать посмотреть (при включенном устройстве) командой lsusb

1
2
3
4
5
6
7
[14:58:54]shlomin@localhost:/tmp>lsusb
Bus 001 Device 002: ID 0bda:0181 Realtek Semiconductor Corp. 
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 002 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 003 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 004 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 005 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
[14:58:54]shlomin@localhost:/tmp>lsusb
Bus 001 Device 002: ID 0bda:0181 Realtek Semiconductor Corp. 
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 002 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 003 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 004 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 005 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub

вот и все. Есть кстати список вендоров тут http://developer.android.com/tools/device.html

dialog. Управление Apache

В журнале «Системный администратор» 2005 год за февраль наткнулся на статью про использование UNIX-утилиты dialog с помощью которой можно делать ASCII-графические приложения для удобного администрирования. Тот пример который был в журнале у меня не работал, поэтому я его модернизировал и вот выкладываю.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#!/bin/sh
dialog --title "Apache interface" 
    --menu "
Данный сценарий управляет web-сервером Apache.
Выберите действие из предложенных ниже:" 15 50 7 
    start "Запуск сервера Apache" 
    stop "Останов сервера Apache" 
    restart ""Жесткий" перезапуск" 
    graceful ""Мягкий" перезапуск" 
    configtest "Тест конфигурационного файла" 2>apctl.tmp
 
UCOMMAND=`cat apctl.tmp`
 
if [[ $UCOMMAND != "" ]]; then
    dialog --title "confirm command" 
        --yesno "Выполнить следующую команду ${UCOMMAND}?" 10 40
 
    if [ $? = 0 ]; then
        if [ $UCOMMAND = "configtest" ]; then
            sudo /usr/sbin/apache2ctl -S
        else
            sudo /usr/sbin/apache2ctl -k $UCOMMAND
        fi
    fi
fi
rm apctl.tmp
#!/bin/sh
dialog --title "Apache interface" 
    --menu "
Данный сценарий управляет web-сервером Apache.
Выберите действие из предложенных ниже:" 15 50 7 
    start "Запуск сервера Apache" 
    stop "Останов сервера Apache" 
    restart ""Жесткий" перезапуск" 
    graceful ""Мягкий" перезапуск" 
    configtest "Тест конфигурационного файла" 2>apctl.tmp

UCOMMAND=`cat apctl.tmp`

if [[ $UCOMMAND != "" ]]; then
    dialog --title "confirm command" 
        --yesno "Выполнить следующую команду ${UCOMMAND}?" 10 40

    if [ $? = 0 ]; then
        if [ $UCOMMAND = "configtest" ]; then
            sudo /usr/sbin/apache2ctl -S
        else
            sudo /usr/sbin/apache2ctl -k $UCOMMAND
        fi
    fi
fi
rm apctl.tmp

Не забываем поменять владельца файла

1
chown root:root ФАЙЛ
chown root:root ФАЙЛ

и дать ему SUID и сделать выполняемым

1
2
chmod 766 ФАЙЛ
chmod +S ФАЙЛ
chmod 766 ФАЙЛ
chmod +S ФАЙЛ

В итоге получаем такую картину:
После выбора нужного пункта появится подтверждение. Нужно нажимать скорее ДА, ты же не делал бы этого если бы был не уверен! Ведь правда?

Фоновое логгирование tcpdump

Пример команды для отлова «значащих» пакетов от конкретного пользователя на конкретный сайт. Размеры от 600 до 700 символов выбраны мной экспериментально. В пакетах такой величины с наибольшей долей вероятности будет содержаться логин и пароль, но конечно при условии если пользователь залогинится после запуска программы. Если он уже в системе, то можно брать cookie, но логин с паролем конечно иметь интереснее.

1
tcpdump -s 0 -l -A dst vkontakte.ru and src 192.168.0.11 and greater 600 and less 700 -w /opt/var/log/vkontakte.log &
tcpdump -s 0 -l -A dst vkontakte.ru and src 192.168.0.11 and greater 600 and less 700 -w /opt/var/log/vkontakte.log &

Привожу пример запроса которые поймался

1
2
3
4
5
6
7
8
9
10
11
12
13
14
POST /?act=login HTTP/1.1
Host: login.vk.com
User-Agent: Mozilla/5.0 (X11; U; Linux i686; ru; rv:1.9.2.12) Gecko/20101026 SUSE/3.6.12-0.7.1 Firefox/3.6.12 GTB7.1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: ru-ru,ru;q=0.8,en-us;q=0.5,en;q=0.3
Accept-Encoding: gzip,deflate
Accept-Charset: windows-1251,utf-8;q=0.7,*;q=0.7
Keep-Alive: 115
Connection: keep-alive
Referer: http://vkontakte.ru/
Content-Type: application/x-www-form-urlencoded
Content-Length: 112
 
from_host=vkontakte.ru&captcha_key=&captcha_sid=&expire=&al_frame=1&email=user%40yandex.ru&pass=password
POST /?act=login HTTP/1.1
Host: login.vk.com
User-Agent: Mozilla/5.0 (X11; U; Linux i686; ru; rv:1.9.2.12) Gecko/20101026 SUSE/3.6.12-0.7.1 Firefox/3.6.12 GTB7.1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: ru-ru,ru;q=0.8,en-us;q=0.5,en;q=0.3
Accept-Encoding: gzip,deflate
Accept-Charset: windows-1251,utf-8;q=0.7,*;q=0.7
Keep-Alive: 115
Connection: keep-alive
Referer: http://vkontakte.ru/
Content-Type: application/x-www-form-urlencoded
Content-Length: 112

from_host=vkontakte.ru&captcha_key=&captcha_sid=&expire=&al_frame=1&email=user%40yandex.ru&pass=password

Это просто «мем» для себя. Параметр -l здесь необходим для того чтобы при демонизации процесса с помощью & программа писала вывод в файл, если не указать, то файл останется пустым. Еще нужно помнить о том, что в этом случае мы не сможем прочитать сами запросы, они запишутся в бинарном виде. А заголовки сможем. Поэтому если нужно читать сами тела запросов, не указывайте параметр -l и не пишите в файл, а пишите на экран, там будет все видно.

p.s. Контакт давно перешел на https и с помощью этого примера поймать авторизацию не удасться

Выделение связанной группы строк компонента Ext.grid.Panel

Недавно понадобилось выделить произвольную группу строк в элементе таблицы grid, решения сходу не нашлось. В основном предлагалось править css-свойства строк, что для меня показалось слишком сложным. Поясню точную задачу которая передо мной стояла: строки в таблице связаны между собой некоторым полем ID_Parent указывающим на поле ID_Operation, т.о. одна строка как бы порождает другую, затем эта другая следующую… При этом строки могут порождать несколько строк параллельно, создавая различные ветки.
Для того чтобы начать работу алгоритма нужно повесить обработчик событий click на таблицу

itemclick: {fn: function(self, record, item, index, e, opts) {}
}

Затем найдем корневую строку в этом списке

root = findRootOperation(record); с помощью функции описанной ниже
function findRootOperation(record) {
parent = store.find("ID_Operation", record.get("ID_Parent"));
  if (parent == -1)
    return record
  else
    return findRootOperation(store.data.getAt(parent))
}

Теперь можно получить весь список строк-детей в массив

var toHighLight = new Array();
function collectionRowIndexes(parent) {  rowIndex = store.find("ID_Operation", parent.get("ID_Operation"));
  toHighLight.push(rowIndex);
  var childIndex = store.find("ID_Parent", parent.get("ID_Operation"));
  while (childIndex != -1) {
    collectionRowIndexes(store.data.getAt(childIndex));
    // Ветки иногда распараллеливаются, поищем еще детей
     var childIndex = store.find("ID_Parent", parent.get("ID_Operation"), childIndex + 1);
  }
}

Все. Проходим по массиву и подсвечиваем нужные строки

for (var key in toHighLight) {  grid.getSelectionModel().select(toHighLight[key], new Boolean(true));
}
toHighLight = new Array();

Собственно все что написано — лишнее, суть только в строке grid.getSelectionModel().select(номер строки, не отчищать другие строки) + нужно не забыть сделать grid.getSelectionModel().setSelectionMode(«MULTI»); т.е. добавить возможность множественного выделения строк.

Последний код следует добавить в itemclick. В итоге получается вот так

Логгирование с повторным использованием параметров

В проекте discounter я использовал простое логирование с помощью модуля logging. Проект состоит из модулей которые пишут свои сообщения в один и тот же файл. Понятно, что можно изменить шаблон логов таким образом для определения того кто пишет

logging.Formatter("%(asctime)-15s %(levelname)-8s [%(moduleName)-10s] %(message)s")

Где moduleName есть имя модуля. Это все довольно просто. Но мне не нравится всегда писать длинные конструкции вроде

self._log.info("Лог в файл %s", var, moduleName=moduleName)

Т.е. вечно пихать в и так длинную строку еще имя модуля. Т.о. предлагаю такое решение: В классе модуля переопределить __getattr таким образом

def __getattr__(self, name):
   try:
        return self._logNamesPart[name]
     except KeyError:
       obj = getattr(self._log, name)
     if obj is not None:
       part = partial(obj, moduleName=self.__class__.__name__)
       self._logNamesPart[name] = part
       return part
     else:
       self._log.error("Попытка вызова несуществующего типа лога "%s"",
         name, moduleName=self.__class__.__name__)

Теперь при создании лога я пишу так

self.debug("Запущен парсер")

и все. При этом создается запись с необходимым именем модуля. Объясню код: существует удобная функция partial с помощью которой можно повторно вызывать функцию не повторяя каждый раз параметры вызова, а указывать только изменившиеся. Получается что при вызове лога с уровнем info, создается partial-функция и сохраняется во внутреннее свойство _logNamesPart, в следующий раз при вызове info, partial будет взят от туда, а если будет вызван другой метод, например debug, он также будет создан и сохранен для последующего использования. В результате я укоротил вызов лога с

self._log.debug("Запущен парсер", moduleName=moduleName)

до

self.debug("Запущен парсер")

Работа с twitter API

Используя twitter API я написал короткую программу которая пишет сообщения в твиттер. Реализована также возможность читать твиты, но она не используется.
Программа размещена на github и работает по принципу UNIX pipe, например:

1
echo "сообщение" | logotwit.py
echo "сообщение" | logotwit.py

Настраивается через конфигурационный файл config.ini
Т.о. я пишу через cron разные системные сообщения сервера и слежу за его состоянием. Примеры сообщений:

1
2
3
4
uptime | logotwit.py # аптайм
sensors | logotwit.py #температура
#проценты остатка места на харде
df | sed "1d" | awk "{print $1,$5}" | tr "\n" "; " | logotwit.py
uptime | logotwit.py # аптайм
sensors | logotwit.py #температура
#проценты остатка места на харде
df | sed "1d" | awk "{print $1,$5}" | tr "\n" "; " | logotwit.py

Замедление работы сети

Используя ip-relay можно замедлить сеть. Пример:

1
ip_relay -b 2048 8033:127.0.0.1:3306
ip_relay -b 2048 8033:127.0.0.1:3306

т.о. натравливаем приложение на порт 8033 и оно будет общаться с тем кто его слушает на порту 3306 через эту программу со скоростью не превышающей 2Б/с
Очень удобно когда нужно смоделировать сильнонагруженную БД или что-либо и посмотреть как на это реагирует приложение.

Работа с удаленными ФС

Иногда нужно смонтировать удаленную файловую систему, например, я разрабатываю ПО которое организовано в виде deb-пакета, причем делаю я это на rpm-based машине, т.е. устанавливать такой пакет будет затруднительно для тестирования. Можно исходные данные поместить на тестовый сервер и править текст на своем любимом редакторе на локальной машине. Пример:

1
sshfs -p 8022 shlomin@127.0.0.1:/home/user/vcard /home/user/server.gates/
sshfs -p 8022 shlomin@127.0.0.1:/home/user/vcard /home/user/server.gates/

Это вообще говоря довольно простой и известный способ, но я хотел указать на то, что специально указал порт 8022, чтобы показать что тот сервер который я хочу юзать находится во внутренней сети доступ к которую есть через третий сервер, а этот самый третий сервер я предварительно проксирую через ssh, например так:

1
sudo ssh -L 8022:192.168.77.80:22 user@server
sudo ssh -L 8022:192.168.77.80:22 user@server

получается ssh слушает 8022 и проксирует трафик через третий сервер на целевую машину 192.168.77.80 уже на 22 порт. Чтобы размонтировать ресурс, достаточно выполнить команду

1
fusermount -u /home/user/server.gates/
fusermount -u /home/user/server.gates/

где /home/user/server.gates/ есть точка монтирования.