SCADA

Posted by Kiril Kirov Sat, 19 Jun 2010 07:21:00 GMT

За да реализираме една SCADA система, най-напред трябва да помислим за комуникацията с различни видове PLC. На пазара за контролери се срещат най-различни производители, предлагащи уреди с най-различни характеристики и десетки комуникационни протоколи. В почти всички случаи данните, които трябва да четем от контролера са малко по обем, в сравнение с паметта на съвременните компютри. Традиционно комуникацията с контролерите е трудна, понеже те могат да бъдат разпръснати на голяма територия. Този проблем е довел до използването през годините на най-различни комуникационни протоколи и преносни среди – голяма част от които изглеждат доста глупаво от съвременна гледна точка. Но тъй като в повечето случаи става дума за производствени процеси – на никой не му и минава през ума да променя каквото и да било. Тоест, принудени сме да работим при тези условия. Когато започнем работа, всякакви предположения за марката контролер и комуникационния протокол, след време ще се окажат неверни (по законите на Мърфи). Можем да се сблъскаме с всичко – от 9600 bps RS232 връзка, с ASCII протокол, до ISO върху Industrial Ethernet. Често пъти библиотеките за комуникация с контролерите не са с отворен код (за щастие при мен случая не е този). Можем да се надяваме, че те предлагат някакъв C интерфейс, който ние да ползваме от някакъв външна програма. Една от първите ни задачи е да реализираме универсален интерфейс към контролерите, който да ни позволява да четем и пишем в тях, без значение каква марка са и какъв протокол използват. Лично аз, дълго време живеех с убеждението, че е най-добре просто да използвам асинхронна комуникация с event loop. Това щеше да работи, ако “драйверите” за различните видове контролери се държаха добре и не променяха настройките на подаваните им файлови дескриптори. За съжаление, това е прекалено оптимистично предположение. Повечето контролери са ужасно бавни, в сравнение със съвременните процесори. Данните могат да се точат “дълго време”, а буферирането в ядрото може да забави получаването им от програмата. Понеже комуникацията се очаква да бъде в “реално време”, а реализацията на различните специфични протоколи върху TCP/IP например, е сложна – можем да очакваме, че голяма част от тези драйвери ползват блокираща, синхронна комуникация. Очевидно блокирането на event loop при отпадане на контролер не е добра идея (особено ако работим със стотици контролери). След обстоен анализ на ситуацията и кратка справка с някои исторически системи, аз се спрях на Reactor pattern за решаване на този проблем. Тъй като искаме да пишем на Ruby, нашата Reactor pattern реализация е EventMachine. За да я ползваме, трябва да напишем Ruby bindings за драйверите на контролерите. Това е доста досадна и неблагодарна работа, но за щастие разполагаме с Extending Ruby главата от Programming Ruby. След няколко дни писане имаме работещи Test::Unit::TestCase(s), които събират данни от контролерите. Как обаче да използваме нашия binding с EventMachine? В общия случай имаме клас PLC, в който има един гаден метод, например fetch, реализиран на C, който може да блокира. EventMachine ни дава метод defer, той приема два аргумента – процедури. Първата процедура е блокираща, тя се изпълнява на отделен thread в реактора, а втората е callback – извиква се с резултата от изпълниение на първата процедура (изпълнява се извън реактора). EventMachine ни дава възможност да работим с нишки индиректно, като по този начин си спестяваме всичките проблеми свързани с директното им използване. Ето една примерна конструкция с defer:

require 'rubygems'
require 'eventmachine'

# Контролер
class PLC

   # Голям, блокиращ C метод
   def fetch
      "result"
   end

end

plc = PLC.new

EM.run do

   # Тази процедура ще изпълняваме в реактора, на отделна нишка
   # Ако всичко е наред, ще четем през една секунда паметта на контролера
   op = proc { sleep 1; plc.fetch }

   # Тук обработваме резултата, и пускаме следващата операция за четене
   cb = proc { |result| puts result; EM.defer(op, cb) }

   # Пускаме първата операция
   EM.defer(op, cb)

end

Естествено, за да работи всичко това, блокиращата операция трябва да има разумен timeout. Той зависи силно от вида контролер, комуникационния протокол и наблюдавания производствен процес. При мен този timeout е от порядъка на 5 секунди. Тъй като EventMachine е реализиран на C/C++, нашия драйвер за контролера също е писан на C, можем да очакваме, че закъсненията от използването на Ruby – динамичен език от високо ниво, са минимални. Гаранции за това обаче няма, много е важно всички компоненти да бъдат тествани много подробно, преди да бъдат пуснати в експлоатация. Официално, системата която аз разработвам се пише на Ruby, но реално зад всеки един ред код на Ruby стоят между 10 и 100 реда код на C. За съжаление, това в случая е неизбежно. Прочетените от контролерите данни отразяват състоянието на реалните обекти, които участват в производствения процес. Връзката между обекти и контролери е основополагаща за всички подобни системи – важно е да имаме максимално гъвкави методи за описание на тази връзка. За целта използваме DSL, който лесно може да бъде реализиран на Ruby. След като имаме добре дефинирани обекти, техните състояния и събитията, които водят до промяната им, можем да започнем да мислим за визуализацията им. За подобна система, би било много глупаво да не използваме Web-базирана технология. Тъй като изображенията на обектите са инженерни чертежи, които трябва да бъдат скалируеми, не можем да ползваме нищо друго, освен SVG. Във всяко едно SVG изображение има определено количество графични обекти. Всеки графичен обект може да бъде в едно от няколко изброени състояния, което се променя във времето. За да визуализираме графиката използваме WebKit прозорец. Този прозорец се създава с помощта на QtRuby, като за целта (леко) се променя WebKit конфигурацията. За да следим измененията на състоянията на обектите във времето трябва да използваме, някакъв вид асинхронна комуникация с WebKit. Тъй като събитията се случват на сървъра, използването на XMLHttpRequest заявки (по-известни като AJAX) не е достатъчно. Клиентът няма как да знае кога дадено събитие се е случило, за да пусне заявката. Разбира се, можем да ползва polling, но е далеч по-елегантно комуникацията да бъде асинхронна. Това което ни трябва е известно, като Reverse AJAX, HTTP Server Push или Comet). За да реализираме каквото и да било Web-приложение, трябва да имаме HTTP сървър. Естествено, той може да бъде нещо съвсем отделно и външно за системата, но тъй като така или иначе, ние имаме работещ EventMachine event loop, че даже и реактор, за комуникация с контролерите – ориентираме се към търсене на EventMachine-базиран HTTP сървър. Както може да се очаква, някой преди нас е написал такъв, казва се Thin. Можем ли да използваме Thin и EventMachine за Comet комуникация с WebKit? Върху този върпос е размишлявал и автора на Thin. Той е написал един Ruby gem, който се казва Pusher и върши точно това което ни трябва. Благодарение на тези технологии, можем да обединим функционалността на HTTP сървъра, Comet сървъра, и модула за комуникация с контролерите. Разбира се, това е просто възможност за интеграция, в някои случаи не е разумно да се прави. Възможно е да “сглобяваме” SVG документите динамично, при всички случаи е трябва да имаме някакво Web приложение на сървъра, което да обслужва заявики към различни URL адреси. Най-удобно за целта можем да използваме Sinatra, или още по-добре Async Sinatra. Вече можем да доставяме SVG документите до клиентите, и да правим асинхронен “push” на съобщения към тях. Това което ще им изпращаме може да бъде XML или JSON съобщение (всъщност може да е всичко). Но най-ефективно от страна на браузера се обработват JSON съобщения, понеже те са просто сериализирани JavaScript обекти. При получаване на JSON съобщение, някакъв JavaScript в браузера трябва да намира елемента от документа, към който се отнася съобщението и да сменя някакви негови атрибути (или да го заменя с друг елемент). В HTML документите тези неща стават много лесно, благодарение на DOM стандарта, както и популярните JavaScript библиотеки от сорта на Prototype, Dojo и т.н., които го разширяват. Тези библиотеки не са много наясно с SVG и различните негови елементи, които могат да се срещнат в документа. Спасението в този случай беше библиотеката Raphaël, тя разширява DOM за SVG документи.

Мисля, че преди време Кнут беше казал, че съвременното програмиране все повече прилича на сглобяване на пъзел. В работата си по този проект, отделих адски много време за проучване на различни, вече реализирани библиотеки и технологии и много малко време за същинско писане на код. Но това не е задължително да бъде лошо.

iPhone hacks 2

Posted by Kiril Kirov Thu, 02 Apr 2009 19:14:00 GMT

Имам си GCC toolchain!
iPhone:~ root# gcc -v
Using built-in specs.
Target: arm-apple-darwin9
Configured with: ../llvm-gcc-4.2/configure --build=x86_64-unknown-linux-gnu --host=arm-apple-darwin9 --enable-static=no --enable-shared=yes --prefix=/usr --localstatedir=/var/cache/iphone-gcc --enable-languages=c,c++,objc,obj-c++ --enable-wchar_t=no --with-gxx-include-dir=/usr/include/c++/4.0.0
Thread model: posix
gcc version 4.2.1 (Based on Apple Inc. build 5555)
I can has debugger. На iPhone разработчиците английския им е като втори майчин език!
iPhone:~ root# cat hello.c 
#include 
int main() {
    printf("hello\n");
    return 0;
}
iPhone:~ root# gcc hello.c
iPhone:~ root# ./a.out 
Killed
iPhone:~ root# dmesg | tail -1
seatbelt: hook..execve() killing pid 242: outside of container && !i_can_has_debugger
iPhone:~ root# ldid -S a.out
iPhone:~ root# ./a.out 
hello
Добре познатото xnu ядро върху ARM S5L8900X
iPhone:~ root# uname -a
Darwin iPhone 9.4.1 Darwin Kernel Version 9.4.1: Sat Nov  1 19:09:48 PDT 2008; root:xnu-1228.7.36~2/RELEASE_ARM_S5L8900X iPhone1,2 arm N82AP Darwin
Процесор 412Mhz , little endian, 32 Kb cache, памет 128 Mb
iPhone:~ root# sysctl hw
hw.ncpu: 1
hw.byteorder: 1234
hw.memsize: 121634816
hw.activecpu: 1
hw.optional.floatingpoint: 1
hw.packages: 1
hw.tbfrequency: 6000000
hw.fixfrequency: 24000000
hw.prffrequency_max: 51500000
hw.prffrequency_min: 51500000
hw.prffrequency: 51500000
hw.memfrequency_max: 137333333
hw.memfrequency_min: 137333333
hw.memfrequency: 137333333
hw.l1dcachesize: 16384
hw.l1icachesize: 16384
hw.cachelinesize: 32
hw.cpufrequency_max: 412000000
hw.cpufrequency_min: 412000000
hw.cpufrequency: 412000000
hw.busfrequency_max: 103000000
hw.busfrequency_min: 103000000
hw.busfrequency: 103000000
hw.pagesize: 4096
hw.cachesize: 0 0 0 0 0 0 0 0 0 0
hw.cacheconfig: 0 16384 16384 1 32 4 0 0 0 0
hw.cpufamily: -1879695144
hw.cpu64bit_capable: 0
hw.cpusubtype: 6
hw.cputype: 12
hw.logicalcpu_max: 1
hw.logicalcpu: 1
hw.physicalcpu_max: 1
hw.physicalcpu: 1
hw.machine = iPhone1,2
hw.model = N82AP
hw.ncpu = 1
hw.byteorder = 1234
hw.physmem = 121634816
hw.usermem = 91435008
hw.pagesize = 4096
hw.epoch = 1
hw.vectorunit = 0
hw.busfrequency = 103000000
hw.cpufrequency = 412000000
hw.cachelinesize = 32
hw.l1icachesize = 16384
hw.l1dcachesize = 16384
hw.tbfrequency = 6000000
hw.memsize = 121634816
hw.availcpu = 1
feedface и асемблер за ARM
iPhone:~ root# objdump -D a.out | head

a.out:     file format mach-o-le

Disassembly of section LC_SEGMENT.__TEXT:

0000000000001000 :
    1000:    feedface     cdp2    10, 14, cr15, cr13, cr14, {6}
    1004:    0000000c     andeq    r0, r0, ip
    1008:    00000000     andeq    r0, r0, r0
    100c:    00000002     andeq    r0, r0, r2
8 Gb флаш
iPhone:~ root# df -h
Filesystem            Size  Used Avail Use% Mounted on
/dev/disk0s1          523M  489M   29M  95% /
devfs                  25K   25K     0 100% /dev
/dev/disk0s2          7.1G  1.2G  6.0G  16% /private/var
Процеси и yellowsn0w
iPhone:~ root# ps ax    
  PID   TT  STAT      TIME COMMAND
    1   ??  Ss     0:02.60 /sbin/launchd
   12   ??  Ss     0:01.55 /usr/sbin/mDNSResponder -launchd
   13   ??  Ss     0:02.38 /usr/sbin/notifyd
   14   ??  Ss     0:03.03 /usr/sbin/syslogd
   15   ??  Ss     0:13.70 /usr/sbin/configd
   17   ??  Ss     2:07.40 /System/Library/CoreServices/SpringBoard.app/SpringBoard
   18   ??  Ss     0:00.70 /usr/bin/yellowsn0w
   20   ??  Ss     0:00.44 /Applications/MxTube.app/MxT2d
   22   ??  Ss     0:00.72 /usr/sbin/update
   23   ??  Ss     0:28.78 /usr/libexec/lockdownd
   24   ??  Ss     0:32.73 /usr/sbin/mediaserverd
   26   ??  Ss     0:02.83 /System/Library/PrivateFrameworks/IAP.framework/Support/iapd
   27   ??  Ss     0:00.29 /usr/sbin/fairplayd
   30   ??  Ss     0:07.88 /System/Library/PrivateFrameworks/CoreTelephony.framework/Support/CommCenter
   31   ??  Ss     0:01.11 /usr/sbin/BTServer
   43   ??  Ss     0:03.12 /Applications/MobilePhone.app/MobilePhone
   77   ??  Ss     0:10.22 /Applications/MobileMail.app/MobileMail
   80   ??  Ss     0:58.92 /Applications/MobileSafari.app/MobileSafari
  100   ??  Ss     0:00.59 /System/Library/Frameworks/SystemConfiguration.framework/SCHelper
  105   ??  Ss     0:00.27 /usr/libexec/ptpd -t usb
  110   ??  S      0:00.38 /usr/libexec/afcd --lockdown -d /var/mobile/Media -u mobile
  112   ??  S      0:00.11 /usr/libexec/notification_proxy
  230   ??  S      0:01.48 /usr/sbin/sshd -i
  271   ??  Ss     0:00.11 /usr/libexec/securityd
  231 s000  Ss     0:00.42 -sh
  272 s000  R+     0:00.02 ps ax
Nmap
iPhone:/ root# nmap 192.168.1.1

Starting Nmap 4.76 ( http://nmap.org ) at 2009-04-02 22:26 EEST
mass_dns: warning: Unable to open /etc/resolv.conf. Try using --system-dns or specify valid servers with --dns-servers
mass_dns: warning: Unable to determine any DNS servers. Reverse DNS is disabled. Try using --system-dns or specify valid servers with --dns-servers
Interesting ports on 192.168.1.1:
Not shown: 997 filtered ports
PORT     STATE  SERVICE
80/tcp   open   http
1900/tcp closed upnp
8080/tcp open   http-proxy
MAC Address: xx:xx:xx:xx:xx:xx (Tp-link Technologies Co.)

Nmap done: 1 IP address (1 host up) scanned in 16.17 seconds
TinyScheme след преправяне на makefile и компилиране
iPhone:~/tinyscheme1.39 root# make
gcc -I. -c -Os  scheme.c 
gcc -I. -c -Os  dynload.c 
gcc  -shared   scheme.o dynload.o  -ldl 
gcc -o scheme -Os scheme.o dynload.o  -ldl 
iPhone:~/tinyscheme1.39 root# ldid -S scheme
iPhone:~/tinyscheme1.39 root# ls -al scheme
-rwxr-xr-x 1 root staff 46224 Apr  2 22:37 scheme*
iPhone:~/tinyscheme1.39 root# ./scheme 
TinyScheme 1.39
> (eval (car (list (quote (display "scheme baby!")))))
scheme baby!#t
Debian rulez
iPhone:~ root# apt-get update
Ign http://www.ispaziorepo.com ./ Release.gpg
Ign http://apt9.ihazsupper.com ./ Release.gpg                                                                                                                                     
Get:1 http://apt.bigboss.us.com stable Release.gpg [186B]                                                                                                                         
Ign http://apt9.yellowsn0w.com ./ Release.gpg                                                                                                                                     
Hit http://www.ispaziorepo.com ./ Release                                                                                                                                         
Get:2 http://apt.modmyi.com stable Release.gpg [189B]                                                                                                                             
Get:3 http://repo.smxy.org xena Release.gpg [189B]                                                                                                                                
Ign http://apt9.ihazsupper.com ./ Release                                                                                                                                         
Ign http://mspasov.com stable Release.gpg                                                                                                                                         
Get:4 http://www.zodttd.com stable Release.gpg [189B]                                                                                                                             
Ign http://www.ispaziorepo.com ./ Packages/DiffIndex                                                                                                                              
Get:5 http://apt.saurik.com tangelo Release.gpg [189B]                                                                                                                      
Ign http://apt9.yellowsn0w.com ./ Release                                                                                                                                         
Hit http://apt.bigboss.us.com stable Release                                                                                                                                      
Get:6 http://apt.modmyi.com stable Release [483B]                                                                                                                                 
Ign http://apt9.ihazsupper.com ./ Packages/DiffIndex                                                                                                                              
Hit http://repo.smxy.org xena Release                                                                                                                                             
Ign http://apt9.yellowsn0w.com ./ Packages/DiffIndex                                                                                                                              
Hit http://www.ispaziorepo.com ./ Packages                                                                                                                                        
Hit http://www.zodttd.com stable Release                                                                                                                                          
Get:7 http://apt.saurik.com tangelo Release [566B]                                                                                                                                
Get:8 http://mspasov.com stable Release [2938B]                                                                                                          
Hit http://apt9.ihazsupper.com ./ Packages                                                                                
Hit http://apt9.yellowsn0w.com ./ Packages                                                    
Ign http://mspasov.com stable/main Packages/DiffIndex                                                               
Ign http://apt.bigboss.us.com stable/main Packages/DiffIndex                                                        
Ign http://apt.modmyi.com stable/main Packages/DiffIndex                                                                                            
Ign http://apt.bigboss.us.com stable/main Packages                                                                                                  
Hit http://repo.smxy.org xena/main Packages/DiffIndex                                                                                                         
Get:9 http://mspasov.com stable/main Packages [1753B]                                                                                                         
Hit http://www.zodttd.com stable/main Packages/DiffIndex                                                                                                       
Ign http://apt.modmyi.com stable/main Packages                                                                                     
Hit http://apt.bigboss.us.com stable/main Packages                                                  
Ign http://apt.saurik.com tangelo/main Packages/DiffIndex                                       
Hit http://www.zodttd.com stable/main Packages                                                  
Hit http://apt.modmyi.com stable/main Packages                   
Hit http://apt.saurik.com tangelo/main Packages                  
Fetched 5745B in 4s (1260B/s)     
Reading package lists... Done

SSH трикове

Posted by Kiril Kirov Mon, 26 Jan 2009 10:53:00 GMT

От извесно време насам ми се върти идеята да започна да пиша на тема компютърни трикове. Причината за това е, че съм почнал да забравям нещата, с които не се занимавам всекидневно. Има работи, които преди години правех с лекота, а в момента дори не мога да си спомня начина, по който съм ги правил. Естествено по-голямата част от тях съм ги намирал в Интернет, но за да ги намеря пак са необходими усилия. Дали тези усилия са по-големи от описването им тук – тепърва ще експериментирам. И така, няма по-универсален инструмент от OpenSSH, ползвам го навсякъде. Ето някои основни неща, които правя с него:

  • Backup на файлови системи
    /sbin/dump -L0 -h0 -u -f - / | /usr/bin/gzip -c | /usr/bin/ssh -c blowfish backup@marvin dd of=eddie-root-backup-`date "+%d.%m.%Y"`.dump.gz
    Идеята е проста – четем файловата система с dump, архивираме данните през тръбата с gzip, подаваме към ssh връзката, а от другата страна ги поемаме с dd. Важно е да не бавим много и двете машини, по тази причина ползваме gzip за архиватор и blowfish за криптиране на връзката (няма по-бърз и толкова сигурен шифър за x86).
  • Отдалечен http proxy сървър
    ssh -L 3128:127.0.0.1:3128 eddie

    По този начин пренасочваме порт 3128 към същия порт на отдалечената машина, през ssh връзката. Интересното в случая е, че този порт там слуша само на localhost интерфейса. Остава да се пусне proxy сървър на този порт (може и нещо друго да се пусне). Един добър избор е 3APA3A tiny proxy – малък е и има много проста конфигурация:

    daemon
    auth none
    external 10.0.0.1
    internal 127.0.0.1
    proxy

    ssh тунела ни спестява автентикация в proxy сървъра, както и един отворен порт на отдалечената машина. Връзката между браузера е proxy сървъра е криптирана.

  • Отадлечени X клиенти или QEMU

    Как да инсталираме Windows в QEMU на отдалечен компютър, до който имаме само ssh? Ами през ssh естествено:

    ssh -YC marvin
    qemu -localtime -hda windows.img -cdrom winxp.iso -boot d -net nic,model=rtl8139 -net tap,script=tapup.sh -m 256
    Ще видим добре познатия син прозорец с инсталацията на Windows. Естествено трябва да имаме работещ X, windows.img файла трябва да е достатъчно голям, а winxp.iso трябва да бъде инсталационния диск. По същия начин можем да пуснем произволен X клиент, като връзката с нашия сървър е криптирана през ssh и никакви отворени портове не ни трябват.
  • Закачване на отдалечена файлова система
    sshfs marvin: /marvin
    В повечето случаи, особено когато не работим в един LAN, NFS не е нужен. Благодарение на FUSE, можем да закачим файлова система на отдалечена машина през ssh връзка. От извесно време работя с MacFUSE и ползвам често отдалечени файлови системи на доста машини.
  • Escape sequences
    ssh -e \~ marvin
    По този начин казваме, че символа след ~ ще се използва за управляващ символ към ssh. Различните управляващи символи са описани в документацията. Използването им е удобно и пести време.