Friday, December 24, 2010

Zabawy w multiboota i odzyskiwanie systemu Windows 7

Gorący post, jestem świeżo po nocce spędzonej nad odzyskiwanie systemu, który znikł ;) ale po kolei. Wczoraj wpadłem na pomysł, aby zainstalować sobie jako drugi system Fedorę na służbowym laptopie. Konsekwencje pierwszego ruchu przepłaciłem wieloma godzinami ślęczenia nad tym laptopem.

Na dysku były dwie partycje, jedna do botowania dla Windows 7, a druga na system. Włożyłem Knoppixa, odpaliłem GParted i zmniejszyłem tą partycję, aby zrobić miejsce na nowy system. Po tej operacji sprawdziłem czy siódemka to przeżyła i okazał się, że nie, ale wtedy jeszcze nie wiedziałem, że partycja w ogóle stała się niewidoczna. O tym dowiedziałem się dopiero jak próbowałem pod montować ją z LiveCD Fedory, gdzie Fedora krzyczała coś o złym ostatnim sektorze. Dane nie były widoczne! W tym czasie podczas próby odpalenia siódemki zgłaszała się konsola odzyskiwania. Długo mi zajęło dojście do tego jak to odkręcić. Okazało się, że rozwiązaniem była modyfikacja tabeli partycji tak, aby partycja, którą zmniejszyłem nadal "rozciągała" się do końca dysku. Wpisałem takie parametry przy pomocy polecenia sfdisk. Po tej operacji dane znów zaczęły być widoczne z Live CD, bo Windows 7 nadal nie startował, a nawet wyglądał na bardziej zepsuty niż był, bo nawet konsola odzyskiwania nie była dostępna i nie była to wina zepsutego MBR-a. Przy okazji tego też "przećwiczyłem" w dwie strony, więc może to przez to.

Następnie ponownie zmniejszyłem partycję ale tym razem nie linuksowym narzędziem a windowsowym ;) Acronis, który dostępny jest na Hiren's Boot CD. Zainstalowałem na nowo utworzonej partycji Fedorę i mogłem dostać się do plików siódemki, ale nadal nie mogłem jej uruchomić. Oczywiście nie miałem oryginalnych płyt instalacyjnych od tego laptopa przy sobie, o instalce Office 2010 nie wspomnę. Podobno można pobrać obraz z serwera Microsoftu jak się ma licencję, ale wszystko było w pracy, a ja to robiłem w domu. Dlatego tak bardzo mi zależało, aby odzyskać istniejącą na dysku instalację.

Ostatecznie udało mi się to zrobić dzięki innej płycie instalacyjnej (też Windows 7 Pro x86) i narzędziu
Bootrec.exe (http://support.microsoft.com/kb/927392)... ufff. Oczywiście było trzeba odbudować magazynu BCD - cokolwiek to znaczy to jest odpowiednikiem gruba w Linuksie. Oczywiście samo odbudowanie jeszcze nic nie dało :), tzn. dało, ale jak później zauważyłem z Live CD nie na tej partycji (czyli pierwszej 100MB), na której powinno to się znajdować - zrobiłem to na systemowej (katalog Boot). Ale to już nie był taki problem, wystarczyło w grubie zmienić, aby nie startował z pierwszej partycji tylko z drugiej i w końcu...

Co mogę powiedzieć po tej zabawie? Na pewno to, iż Windows nie jest takim surowym (raw) systemem jak Linux. Czyli stworzenie w czymkolwiek partycji, wrzucenia gruba i plików ze starego systemu do nowo utworzonej partycji pod Linuksiem załatwiłoby sprawę, a tu tak nie ma.
Poza tym dawno już się nie bawiłem w konfigurowanie Fedory na desktop i jak na nowo doceniłem Debiana i wydanie stabilne, które po prostu działa.

Wednesday, December 22, 2010

!Debian Community

To blog zdecydowanie techniczny, ale dlaczego nie pisać tu o innych problemach, nie koniecznie technicznych, takich jak aspekty uczestnictwa w społeczności technicznej?
Dla mnie osobiści to nie nowość, nie raz przerabiałem ten problem - jego sens. Pozwolę sobie przytoczyć mały cytat - a co, muszę się pożalić - z mojej strony GNU, Linux, Debian:
Uzyskasz pomoc od społeczności
Społeczność Debiana jest bardzo aktywna i chętnie pomaga swoim użytkownikom. W sieci jest wiele materiałów na temat systemu Linux. Na polskim forum Debiana dziennie pojawia się kilkadziesiąt postów. Jeżeli będziesz miał problem z systemem lub sprzętem to istnieje duże prawdopodobieństwo, że rozwiązanie zostało już opisane. Możesz uzyskać odpowiedz na forum, grupach dyskusyjnych poświęconychDebianowi oraz ogólnie Linuksowi lub oficjalnej polskiej liście mailingowej. Musisz jednak pamiętać, że osoby pomagające robią to tylko z dobrych chęci.
Tak właśnie napisałem w tamtym roku. Pozwoliłem sobie pogrubić pewien fragment, mówiący o tym, kiedy można liczyć na społeczność Debiana. Trudno mówić w imieniu społeczności, ale jestem jej aktywnym członkiem i pozwolę sobie opisać tutaj pewne przykre sytuację, które niestety mają miejsce.

Szczerze powiem, że sam jestem zadowolony, z tego jak to wtedy ująłem: "Jeżeli będziesz miał problem z systemem lub sprzętem" patrząc na to dzisiaj. Doskonale pasuje to do tematu, który chcę poruszyć, a mianowicie chamstwa w tej społeczności i pytania czy warto się dla niej poświęcać.

Jak wiadomo Linux jest otwarty dla wszystkich i dzięki temu zdobył taką popularność, ale to nie znaczy, że nie ma wad, że ten medal nie ma drugiej strony. Prawda jest tak, że kończy mi się piwo i nie mam co robić, albo mam już wszystkiego dość :). Poważnie to jest to temat wart pisania, ostatnio dużo nad tym myślałem. Szukałem złotego środka, kompromisu między udzielaniem się w społeczności, a robieniem czegoś dla siebie. Wynikiem tego myślenia jest ten blog, ale zanim o tym zacznę pisać pozwolę sobie przytoczyć świeży przykład niewdzięczności i demotywacji http://debian.linux.pl/threads/21292-BASH-wyra%C5%BCenia-regularne-grepa.

To wątek na forum, w którym autor zarejestrowany w grudniu tego roku i posiadający tylko 4 wypowiedzi i tylko w tym jednym wątku. Mówiąc krótko zarejestrował się tylko po to, aby uzyskać rozwiązanie. Pisze na początku, że szuka sposobu na coś, nie ważne czego. Dostaje rozwiązania - trzy, w tym moje w AWK. Po czym pisze, że potrzebuje rozwiązania, które wykorzystuje tylko wybranych narzędzi itp., aha praca domowa! O nie. Odpisałem, że my tu nie rozwiązujemy prac domowych, a przynajmniej ja nie będę poświęcał swojego czasu na szukanie jak to samo zrobić w narzędziach, których ma użyć. Jaką dostałem odpowiedz można sobie przeczytać. Wkurza mnie to, że jeszcze mu pomogłem, bo problem został w sumie rozwiązany. Powiem krótko, gdybym nie był redaktorem tego portalu i nie wiedział, że ta odpowiedz zostanie zaraz usunięta odpisałbym mu podobnie jak w kawałku "Na spidzie": ja Cię serdecznie pieprze! To właśnie po to powstał ten blog, abym mógł tu pisać co mi się tylko podoba, a nie co wypada, a czego nie.

Nie dość, że pomagasz ludziom, nic z tego nie masz, robisz do dla idei, to jeszcze musisz wysłuchiwać takich komentarzy. Co do uczenia się podczas rozwiązywania problemów innych to szczerze powiem, że rzadko się to zdarza. Zdecydowanie więcej się uczę przerabiając konkretny temat. Owszem, jak temat jest ciekawy można się coś nowego nauczyć szukając rozwiązania, ale w ponad 90% przypadków jest to czytanie rzeczy, które już wiesz, albo Cię nie interesują. Można to porównać do kupowania gazet o tematyce informatycznej, a książek. Pięć gazet nie nauczy Cię tyle, co dobra książka o konkretnym temacie, a kosztuje tyle samo.

Tak więc proszę się nie dziwić takim tekstom np. na stronie Jak mądrze zadawać pytania:
Nie zamierzamy poświęcać naszego czasu na odpowiedzi ludziom niechętnym do samodzielnego myślenia - odróbcie więc swoją pracę domową, zanim zadacie jakiekolwiek pytanie. Tacy ludzie są pożeraczami naszego czasu - zabierają go nam bez opamiętania, marnują każdą chwilę, którą moglibyśmy poświęcić innemu, bardziej interesującemu zagadnieniu lub osobie, która bardziej zasługuje na naszą odpowiedź. Tych pierwszych nazywamy 'łajzami' ('losers', z historycznych względów wymawiane często jako 'lusers').
Przytoczona sytuacja to tylko wierzchołek góry lodowej, takim przypadkom w zasadzie nie powinno się poświęcać tyle czasu jak ja tu to robię. Niestety temat jest o wiele bardzie nieprzyjemne i poważniejszy. W 2007 roku portal jakilinux.org opublikował artykuł (tłumaczenie) Dlaczego odszedłem — wywiad z deweloperem jądra Conem Kolivasem, z którego można wyczytać, że problemy istnieją - w tym wypadku trochę innego kalibru.
Jestem redaktorem portalu debian.linux.pl i mam tam również swój blog, do niedawna chętnie dokonywałem w nim wpisów. Powiem szczerze, że decyzja o stworzeniu nowego bloga była dojrzała, nie była pochopna podjęta pod wpływem emocji. Była bardziej wyryta przez sytuacje, które niestety regularnie się powtarzały. Sam byłem świadkiem tego jak jedne z moderatorów zrezygnował ze swojej fuchy po tym, jak otrzymał od kolegi po fachu pewną wiadomość. Sam również tego doświadczyłem, dostosowałem się do uwag, ale nie podobało mi się to zupełnie. Tak właśnie powstał ten blog i świadomość jaki jest optymalny sens społeczności i udzielania się w niej. Miałem duży dylemat. Wiedziałem, że chcę mieć swoje "podwórko", gdzie nikt nie będzie mi mówił jak mam pisać pewne zwroty, a z drugiej strony wiedziałem, że te same teksty, które tam publikuję na prywatnym blogu nie będą odwiedzane przez taka dużą ilość osób. Jak wiemy, nie ma większej zapłaty niż rekordowy licznik odwiedzin ;).

Tak więc musiałem się nauczyć, że społeczność owszem, ale tylko wtedy, gdy z danym tekstem chcę trafić do wielu odbiorców, taki multicast. Myślałem, że na tamtym blogu będę mógł pisać to, co nie nadaje się na artykuł, ale mimo wszystko zawiera przydatne informację. Myliłem się, nie czułem się tam wolny. Poza tym chciałem mieć coś tylko swojego, np. notatki dla siebie.

Oczywiście nie zamierzam tutaj stwierdzić, że nie warto, że nie ma sensu, ale trzeba być wyrafinowanym, czy nam się to podoba czy nie. Pamiętam jak od czasu do czasu natrafiałem na informację, że developerzy rzadko odpowiadają na e-maile, albo strona www jakiegoś projektu nie była aktualizowana przez dłuższy czas (mimo, iż kod rozwijany jest na bieżąco). Wygląda na to, iż osoby wkładające najwięcej wysiłku w świat open source wolą swój kod od kontaktów z jego "fanami".

Sunday, December 12, 2010

Świat według Google, czyli "live within the browser"

Już za czasów Netscape wyobrażano sobie taką przyszłość, iż przeglądarka stanie się platformą i Windows przestanie być dominującą platformą. W połowie lat 90 nie było jeszcze wystarczająco rozwiniętych technologii ale dziś już są i Google to pokazuje.

Wizja Google jest następująca: wszystko jest w sieci, użytkownikowi do życia wystarczy tylko przeglądarka.
Wszystkie dane oraz kod aplikacji będzie przechowywany na serwerze, laptop Cr-48 jest tylko terminalem - jak kiedyś.
Tak więc mamy klienta (Chrome OS) i serwer (Linux LAMP). Użytkownik chcący skorzystać z sieć nie musi być administratorem swojego komputera. Administracja będzie scentralizowana a używanie aplikacji biznesowych oraz przestrzeni dyskowej płatne.

Oto film demonstrujący jak bezpieczne są nasze dane w takim modelu.


A tu Google przedstawia swoją wizje.



Co ciekawego tam zobaczymy? Hy
Loginem do notebooka z Chrome OS jest nasze konto w Google


Google ma nadzieję w ciągu 10-15 lat przepisać większość aplikacji w technologii Web. W ich wyszukiwaniu oraz zakupie ma pomóc witryna chrome.google.com/webstore.


Dla biznesu specjalistyczne potrzeby będą dostarczane poprzez pulpit zdalny do wirtualnych serwerów z aplikacjami. Tym ma się zając firma Citrix, czyli XenServer. Ma to na celu oszczędzić pieniądze i sprzęt.



Tak więc za parę lat nic nam nie będzie potrzebne poza dostępem do sieci.


Jedyną rzeczą, która mnie zainteresowała w tej koncepcji to tzw. back-end, czyli Google App Engine działający na Linuksie krótko mówiąc.

Ważną rolę będą odgrywać również technologie po stronie klienta jakie jak HTML5 i JavaScript.

Saturday, December 11, 2010

Czego szukał świat w 2010 roku

Linux kernel exploit - full-nelson.c

 Dan Rosenberg na liście Full-Disclosure opublikował kod exploita, który podobno potrafi zdobyć uprawnienia roota z lokalnego systemu. Przetestowałem jego kod na trzech systemach:

Debian Squeeze

na serwerze
Debian Lenny

i Fedorze 13
Fedora 13
Jak widać na żadnym się nie udało, mimo iż podatność dotyczy Linux Kernel <= 2.6.37, ale...



 *  * The particular symbols I resolve are not exported on Slackware or Debian
 *  * Red Hat does not support Econet by default
 *  * CVE-2010-3849 and CVE-2010-3850 have both been patched by Ubuntu and
  *     *  Debian

 http://www.securityfocus.com/bid/45072

Przy okazji usłyszałem o pewnej opcji sysctl, która poprawia bezpieczeństwa:

modules_disabled:

A toggle value indicating if modules are allowed to be loaded
in an otherwise modular kernel.  This toggle defaults to off
(0), but can be set true (1).  Once true, modules can be
neither loaded nor unloaded, and the toggle cannot be set back
to false.

Wednesday, December 8, 2010

Przeciążenie operatora przypisania

Podczas pisania programu zbiór klas, które stworzyłem dosyć się rozrósł i nieuchronnie sposób kodowania zmierzał do korzystania z zaawansowanych właściwości języka C++. Pierwszym wartym opisania jest operator przypisania, czyli "=". Dlaczego warto go używać, a raczej trzeba.

Po pierwsze zacząłem programować bardziej w stylu programowania kompozycyjnego niż obiektowego - jeszcze nie wiem czy to dobry pomysł. Zastanawiam się nad wprowadzeniem dziedziczenia ponieważ zdarza się, że muszę definiować takie same metody w różnych klasach np. set_name(string).
Operator przypisania (operator=) jest przydatny podczas takiego projektowania:

class A
{
int a;
};

class B
{
A _a;
public:
void set_a (A);
void set_a2 (int);
};

Jak widzimy mamy dwie wersję funkcji set_a(). Pierwsza pobiera argument typu a więc w definicji funkcji zostaje tylko przypisać do do tego mieszczącego się w klasie b.

void B::set_a (A _newa)
{
_a = _newa;
}

I tu pojawia się potrzeba implementacji operatora przypisania.
Z drugiej zaś strony, gdy chcielibyśmy ukryć wewnętrzną budowę klasy A (hy po co...) należałoby użyć drugiej wersji metody


void B::set_a2 (int _newval)
{
// np. set()
_a.set(_newval);;
}

Problem pojawia się wtedy, gdy klasa A ma wiele właściwości i nadal "rośnie". Wtedy prototyp i definicja metody nie wygląda czytelnie.

Tak, teraz mamy dwie drogi. Albo definiujemy klasę B jako dziedziczącą po A i mamy w klasie B bezproblemowy dostęp do składowych klasy A w sekcji protected, albo używamy kompozycji. Mimo wszystko dziedziczenie nie rozwiązuje problemu kopiowania obiektów, które de fakto zawierają wiele danych. Tworzenie dynamicznych obiektów i wskaźniki do nich jako składowe w tym przypadku uprościłyby sprawę. Wtedy nie ma potrzeby kopiowania składowej po składowej. Usuwamy starą pamięć i do wskaźnika przypisujemy wartość nowego obiektu. Podpatrzyłem taki pomysł w źródłach projektu Scintilla w pliku Editor.h:

Editor &operator=(const Editor &) { return *this; }

Ta definicja przeciążonego operatora bardzo mi się podoba jednak moja jeszcze jest inna:

class Location
{

private:
string    name;
t_classroom classroom_table;

public:
Location& operator= (const Location& cp);
};


Location& Location::operator= (const Location& cp)
{
if (this != &cp)
{
name = cp.name;
classroom_table = cp.classroom_table;
}
return *this;
}

Przy okazji odkryłem pewną właściwość C++, o której nie wiedziałem, albo nie pamiętam. Jak widzimy zmienna name jest prywatna, a jednak w definicji operatora używamy jej jakby była publiczna. Sprawdziłem to pisząc odpowiedni kod i okazuje się, że prywatne dane dla innego obiektu tego samego typu wcale nie są taki prywatne. Dopiero kiedy spróbujemy użyć prywatnej zmiennej w metodzie innej klasy zobaczymy komunikat o tym, iż zmienna jest prywatna.

Pseudokod ułatwi zrozumienia działania przeciążenia operatora przypisania:

Location a, b;
//a.operator=(b);
a = b;

Tak to wygląda po rozwinięciu przez kompilator. Teraz pytanie dlaczego referencje? Aby nie kopiować argumentu formalnego funkcji. Dlaczego nie wskaźnik? Bo operacja przypisania wtedy wyglądałaby tak:

&a = &b;

Prawda, że ładnie?
Jeszcze dlaczego argument jest const? Aby nie zmienić jego zawartości.

Należy pamiętać o tym, że każdy typ, z którego korzystamy w nowym typie, gdzie definiujemy operator przeciążenia również musi posiadać taką funkcji, ponieważ będzie ona w takim przypadku wywoływana rekurencyjnie.

Sunday, December 5, 2010

Personalizacja BackTrack na VirtualBox

Po instalacji na wirtualnym dysku:

login: root
hasło: toor

startx


Instalujemy VBoxLinuxAdditions, umożliwi m.in. ustawienie większej rozdzielczości



Ustawiamy sieć, w moim przypadku to sieć wewnętrzna za innym linuksem na wirtualu


/etc/network/interfaces

auto eth0
#iface eth0 inet dhcp
iface eth0 inet static
        address 192.168.44.44
        netmask 255.255.255.0
        network 192.168.44.0
        broadcast 192.168.44.255
        gateway 192.168.44.1








Ustawiamy automatycznie uruchomianie konfiguracji sieci

update-rc.d networking defaults





Montujemy zasób sieciowy. Można manualnie:

mount -t vboxsf vboxshare /mnt/

lub automatycznie


/etc/fstab
vboxshare       /mnt/vboxshare  vboxsf  defaults        0       0

Saturday, December 4, 2010

TCP SYN Flooding

Najpierw RFC 4987


Rozpoznanie

Hping ma opcję -i (interval), która określa jak często mają być generowane i wysyłane pakiety.

-i X
X to sekundy albo uX mikrosekundy, czyli 1/1000000s.

Ale ale, czytamy dokładnie: "Wait the specified number of seconds or  micro  seconds  between sending  each  packet".


Czyli to odstęp wyrażony w s. albo μs.

--fast    
             Alias for -i u10000. Hping will send 10 packets for second.

--faster
              Alias  for -i u1. Faster then --fast ;) (but not as fast as your
              computer can send packets due to the signal-driven design).


 --flood
              Sent packets as fast as possible, without taking care to show
              incoming replies.  This is ways faster  than  to  specify  the
              -i  u0 option.


Hy jeżeli 1μs to 1 000 000 część sekundy, a odstęp między pakietami wynoś 10 000 to czemu wychodzi 10 pakietów?

1 000 000 / 10 000 = 100 a nie dziesięć.

Sprawdziłem opcję --fast i rzeczywiście wysyła dokładnie 10 pakietów na sekundę, a przecież 10 000 x 10 to 100 000 a nie milion! Chyba wynika to z tego, że wysłanie jednego pakietu zajmuje karcie 10 mikrosekund (100/10) i wtedy rzeczywiście wychodzi 10 pakietów. W takim razie 1 000 000 / 10 mówi nam, że karta może wysłać maksymalnie 100 000 pakietów - teoretycznie.

Walimy
35263÷9,569=3685,129062598 pakietów na sekundę.

Na Debianie, na Windows XP na port SMB system stopował całkowicie.

Tcpdump

fedora1 port > 4700  --SYN--> 192.168.42.1:22 // chcę pogadać
192.168.42.1:22  --SYN-ACK--> fedora1 port > 4700 // OK
fedora1 port > 4700  --RST--> 192.168.42.1:22 // a już mi się nie chcę :)


Nawiązane połączenia

Thursday, December 2, 2010

Personalizacja SciTE

Co zmieniamy w .SciTEUser.properties:
# Globals
position.width=800
position.height=960
horizontal.scrollbar=0
toolbar.visible=0

# Element styles
caret.width=2
caret.line.back=#FFFED8

# Sizes and visibility in edit pane
line.margin.visible=1

# Indentation
tabsize=4
indent.size=4

# Unicode
code.page=65001

# Checking
save.session=1

# Global default styles for all languages
if PLAT_GTK
font.base=font:!Bitstream Vera Sans Mono,size:9

# Default
style.*.32=$(font.base),back:#FAFCFA,fore:#000000

Kopiujemy plik cpp.properties do katalogu domowego (tego samego, w którym znajduje się .SciTEUser.properties) i zmieniamy:

keywordclass.cpp2=cout cin string vector pair list iterator

# keywords2 is for highlighting user defined keywords or function calls or similar
#keywords2.$(file.patterns.cpp)=file
keywords2.$(file.patterns.cpp)=$(keywordclass.cpp2)

Na razie wystarczy.

http://www.scintilla.org

Monday, November 29, 2010

Operacja dzielenia w Pythonie

Pythona pobierznie znam od ok. 4 lat, podobał mi się gdy używałem biblioteki GTK+. Dlatego chciałem lepiej się z nim zapoznać, między innymi w tym celu miałem napisać program w nim dla wprawy. Algorytm programu opiera się na działaniach na liczbach z przecinkiem. Jedną z pierwszych rzeczy jaką trzeba zrobić to policzyć jakim procentem sumy trzech zmiennych jest każda ze zmiennych.
Napisałem prosty skrypt w Pythonie zawierający kod:


liczba_h_w_piatek = 3
liczba_h_w_sobote = 5
liczba_h_w_niedziele = 5

procent_h_dla_piatku = liczba_h_w_piatek/(liczba_h_w_piatek+liczba_h_w_sobote+liczba_h_w_niedziele)

print "%.2f" % (procent_h_dla_piatku)

W wyniku otrzymałem "0.00", co jest? Python mnie powalił, a miało być prościej.


Okazało się, że Python v < 3  ma z tym problem! Wkurzyłem się i napisałem to w C++:
#include <iostream>

using namespace std;

int main ()
{
float p=3;
float s=5;
float n=5;

cout << p /(p + s + n) << endl;
return 0;
}

Czy to aż tak strasznie? Chyba nie.
Zdecydowałem pisać ten program w C++. Będę musiał zbudować typy danych ale sam algorytm przetwarzania danych wcale nie będzie dłuższy. Rok temu pisałem w C++ z użyciem GTK+ kalkulator i kod wyszedł dosyć znośnie: http://pl.wikibooks.org/wiki/GTK+

Sunday, November 28, 2010

Fedora SECURITY LAB

Fedora wprowadza najwięcej nowość. Tu mamy specjalną dystrybucję do testowania bezpieczeństwa.



Oto lista pakietów, czyli ładny spis: https://fedorahosted.org/security-spin/wiki/availableApps

Saturday, November 27, 2010

Windows SteadyState

Uff, trochę się naszukałem, aby ponownie to znaleźć. Tak to już jest, kiedyś tego używałeś, a teraz jest Ci potrzebne i nie pamiętasz jak się nazywa. Dlatego właśnie powstał ten blog, aby ocalić swoje doświadczenia od zapomnienia.

Program chroni dysk przed zapisem i przydaje się gdy "Whether you manage computers in a school computer lab or an Internet cafe, a library..."

Hy, no cóż - kiedyś ta kategoria musiała powstać: Windows :)

Historia przeglądarek

Znalazłem oś czasu rozwoju przeglądarek, taką jak dla systemów UNIX: http://en.wikipedia.org/wiki/File:Timeline_of_web_browsers.svg
Pomyśleć, że start miął miejsce ok. 1995!


Znacie tą historię jak wielkie E wykańcza konkurencję, czyli Netscape w wyniku czego powstaje nowy projekt, którego nazwa jest akronimem zemsty? Jeżeli nie to polecam poniższe filmy.


Bitwa przeglądarek


Wojna wyszukiwarek


eBay i Amazon


Świat według Google

Saturday, November 20, 2010

fmt - formatowanie szerokości tekstu

Jak formatuje się tekst, aby wyglądał tak jak na grupach dyskusyjnych bądź listach mailingowych? Poleceniem fmt: http://en.wikipedia.org/wiki/Fmt

fmt to przy okazji. Ciekawa jest dokumentacja GNU Coreutils (http://www.gnu.org/software/coreutils/manual/) gdzie o fmt również przeczytamy, czyli jest częścią tego pakietu - warto mu się przyjrzeć bliżej :)

Kompresja bzip2, xz, 7-Zip

grzesiek@home:~$ ls -lh kalkulator-0.0.1.ogv
-rw-r--r-- 1 grzesiek grzesiek 46M 2009-12-01  kalkulator-0.0.1.ogv

grzesiek@home:~$ time bzip2 -z9 kalkulator-0.0.1.ogv 
real 0m10.539s
user 0m10.425s
sys 0m0.108s

grzesiek@home:~$ ls -lh kalkulator-0.0.1.ogv.bz2
-rw-r--r-- 1 grzesiek grzesiek 44M 2009-12-01  kalkulator-0.0.1.ogv.bz2

grzesiek@home:~$ time bzip2 -d kalkulator-0.0.1.ogv.bz2
real 0m5.772s
user 0m5.608s
sys 0m0.140s

grzesiek@home:~$ time xz -z9 kalkulator-0.0.1.ogv 
real 0m22.685s
user 0m22.249s
sys 0m0.420s

grzesiek@home:~$ ls -lh kalkulator-0.0.1.ogv.xz
-rw-r--r-- 1 grzesiek grzesiek 34M 2009-12-01  kalkulator-0.0.1.ogv.xz

grzesiek@home:~$ time xz -d kalkulator-0.0.1.ogv.xz
real 0m3.283s
user 0m3.168s
sys 0m0.112s

grzesiek@home:~$ xz
xz       xzcat    xzcmp    xzdiff   xzegrep  xzfgrep  xzgrep   xzless   xzmore

grzesiek@home:~$ time 7z a -mx=9 kalkulator-0.0.1.ogv.7z kalkulator-0.0.1.ogv
7-Zip 9.04 beta  Copyright (c) 1999-2009 Igor Pavlov  2009-05-30
p7zip Version 9.04 (locale=pl_PL.UTF-8,Utf16=on,HugeFiles=on,4 CPUs)
Scanning

Creating archive kalkulator-0.0.1.ogv.7z

Compressing  kalkulator-0.0.1.ogv    

Everything is Ok

real 0m10.705s
user 0m20.781s
sys 0m0.472s

grzesiek@home:~$ ls -lh kalkulator-0.0.1.ogv*
-rw-r--r-- 1 grzesiek grzesiek 46M 2009-12-01  kalkulator-0.0.1.ogv
-rw-r--r-- 1 grzesiek grzesiek 34M 11-20 21:09 kalkulator-0.0.1.ogv.7z

grzesiek@home:~$ mv kalkulator-0.0.1.ogv _kalkulator-0.0.1.ogv
grzesiek@home:~$ time 7z e kalkulator-0.0.1.ogv.7z

7-Zip 9.04 beta  Copyright (c) 1999-2009 Igor Pavlov  2009-05-30
p7zip Version 9.04 (locale=pl_PL.UTF-8,Utf16=on,HugeFiles=on,4 CPUs)

Processing archive: kalkulator-0.0.1.ogv.7z

Extracting  kalkulator-0.0.1.ogv

Everything is Ok

Size:       48065852
Compressed: 34814347

real 0m3.663s
user 0m3.572s
sys 0m0.092s


Obydwa rozwiązania korzystają z LZMA:

Thursday, November 18, 2010

"if ($answer =~ /Y|y/)" Python też tak może

import re
import sys

print "...[Y/N]?"
answer = sys.stdin.read(1)
if re.search("Y|y",answer):
print "tak"
elif re.search("N|n",answer):
print "nie"

Monday, November 15, 2010

"\1" pierwszy zapamiętany wzorzec

grzesiek@home:~$ echo "ABCD" | sed -r 's/(.)/  \1/g'
  A  B  C  D
grzesiek@home:~$ echo "ABCD" | sed -r 's/(.)/\1 /g'
A B C D
grzesiek@home:~$ echo "ABCD" | sed  -r 's/(.)([ABCD])/\1 -\2/g'
A -BC -D




http://www.linuxquestions.org/questions/programming-9/how-to-insert-characters-into-a-string-in-shellscripts-844413/
http://www.grymoire.com/Unix/Sed.html#uh-4
http://www.regular-expressions.info/

Sunday, November 14, 2010

Implementacja motta UNIX


Perl:

$live = 1;
$free = 0;

print "Czy jesteś wolnym człowiekiem [Y/N]?";
$answer = getc(STDIN);

if ($answer =~ /Y|y/) {$free=1;}
else {$free=0;}

$live and $free or die("Jesteś użytkownikiem Micro\$hit? :)\n");
print "OK\n";



Python:

import sys

live=1
free=0

print "Czy jestes wolnym czlowiekiem [Y/N] ?"
answer = sys.stdin.read(1)

if (answer == 'Y') or (answer == 'y'):
    free=1
else:
    free=0

live and free or exit() 
print "OK\n"


Saturday, November 13, 2010

Perl na wesoło




Już od dłuższego czasu o tym myślałem, chyba nie mam wyjścia :)

Wednesday, November 10, 2010

Historia poleceń

Wprowadzasz dużo poleceń testując nową 'zabawkę'? Często musisz wykonać jeszcze raz prawie to samo polecenie - długie polecenie?

history

Aby wykonać jeszcze raz komendę np. nr 543 możesz wpisać:

!543


Aby wykonać ponownie ostatnią komendę zaczynająca się np. od polecenia cat można wpisać:

!cat


Mała rzecz, a cieszy.

Saturday, November 6, 2010

asciiart/cpu.txt



Richard Stallman i jego analogie do wolnego oprogramowania odpadają :)

ddosim - symulator DDOS


Description
========
DDOSIM simulates several zombie hosts (having random IP addresses) which
create full TCP connections to the target server. After completing the
connection, DDOSIM starts the conversation with the listening application
(e.g.HTTP server).Can be used only in a laboratory environment to test the
capacity of the target server to handle application specific DDOS attacks.

Features
======
   - HTTP DDoS with valid requests
   - HTTP DDoS with invalid requests (similar to a DC++ attack)
   - SMTP DDoS
   - TCP connection flood on random port


http://stormsecurity.wordpress.com/2009/03/03/application-layer-ddos-simulator/
http://sourceforge.net/projects/ddosim/

Man-In-The-Middle

Understanding Man-In-The-Middle Attacks - http://seclists.org/fulldisclosure/2010/Nov/20



Friday, November 5, 2010

Kopiowanie pomiędzy hostami

Polecenia systemu Unix są dobrym tematem, zwłaszcza, że zawsze znajdzie się jakieś, o którym się nie wiedziało. Przyznam, że od nie dawna zacząłem używać polecenia scp. Polecenie to niby jest proste w działaniu ale za to jak może ułatwić pracę.
Często testuje się jakieś konfiguracje na testowym systemie, np. w domu ;). Po udanych testach pozostaje wdrożyć tą funkcjonalności na serwerze produkcyjnym i tu przydają się już wcześniej przygotowane pliki z serwera testowego.
Proste polecenie przesyła przykładowy plik z testowego na produkcyjny serwer:
scp -P 7233 /usr/share/dansguardian/languages/ukenglish/template.html admin@prv.domena.pl:template.html

Ciekawą koncepcją jest też wprowadzanie poleceń do historii bez ich faktycznego wykonywania, czyli poprzedzając je znakiem #, co oznacza komentarz dla interpretera powłoki bash. Taka żółta karteczka...

man scp

Thursday, November 4, 2010

"icmp-host-prohibited"?


Znalazłem taką opcje w zaporze RH Fedory, niby domyślna polityka ustawiona jest na ACCEPT a jednak ładnie wszystko odrzuca. Postanowiłem zbadać tą sprawę. Gdzieś w sieci natrafiłem na link do RFC1812, gdzie można trochę o tym poczytać:
5.3.9 Packet Filtering and Access Lists
...
   The router SHOULD allow an appropriate ICMP unreachable message to be
   sent when a packet is discarded.  The ICMP message SHOULD specify
   Communication Administratively Prohibited (code 13) as the reason for
   the destination being unreachable.

Przeczytałem gdzieś również, że ktoś po testach stwierdził, iż rzeczywiście używanie tej opcji dla odrzucanych pakietów oszczędza pasmo. Postanowiłem również to przetestować.

Test jest prosty, na serwerze dodałem na samym końcu regułę:
iptables -A INPUT -j REJECT --reject-with icmp-host-prohibited

Następnie użyłem prostego badania zamkniętego portu, pierwszy wynik jest baz powyższej reguły, a drugi już z:

root@home:/home/grzesiek# nmap x.xxx.pl -p 33 -PN

Starting Nmap 5.00 ( http://nmap.org ) at 2010-11-04 21:17 CET
Interesting ports on x.xxx.pl (XX.18.194.XXX):
PORT   STATE    SERVICE
33/tcp filtered dsp

Nmap done: 1 IP address (1 host up) scanned in 2.20 seconds

Trwało to 2 sekundy, ale długo :)


# tcpdump -i eth0:1 port 33
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0:1, link-type EN10MB (Ethernet), capture size 96 bytes
21:16:54.948070 IP PC-77-XX-24-XXX.xxx-net.pl.45056 > x.xxx.pl.33: S 2889941185:2889941185(0) win 2048 <mss 1460>
21:16:55.949049 IP PC-77-XX-24-XXX.xxx-net.pl.45057 > x.xxx.pl.33: S 2889875648:2889875648(0) win 2048 <mss 1460>
^C
2 packets captured
2 packets received by filter
0 packets dropped by kernel


Zostały odebrane dwa pakiety.
Teraz wersja z wysyłaniem przez serwer komunikatu zwrotnego ICMP:

root@home:/home/grzesiek# nmap x.xxx.pl -p 33 -PN

Starting Nmap 5.00 ( http://nmap.org ) at 2010-11-04 21:52 CET
Interesting ports on x.xxx.pl (83.18.194.164):
PORT   STATE    SERVICE
33/tcp filtered dsp

Nmap done: 1 IP address (1 host up) scanned in 0.17 seconds

Od razu widać - szybciej.


# tcpdump -i eth0:1 port 33
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0:1, link-type EN10MB (Ethernet), capture size 96 bytes
21:51:59.989134 IP PC-XX-46-24-XXX.xxxx-net.pl.55250 > x.xxx.pl.33: S 868347170:868347170(0) win 1024 <mss 1460>
^C
1 packets captured
1 packets received by filter
0 packets dropped by kernel

Drugi pakiet był już zbędny, ponieważ nmap dostał jasny komunikat dlaczego port nie odpowiada - administracyjnie zabroniony.


Blokowanie indeksowania

Dziś przekonałem się - całe szczęście nie na swojej skórze, jak ważna jest konfiguracja indeksowania serwisu, którym się zarządza. Tak samo jak w Moodle podczas instalacji jesteśmy informowani o tym, iż katalog z danymi nie powinien znajdować się w ścieżce udostępnianej przez serwer WWW, tak i w serwisie musimy zadbać o to, aby dane udostępniane w podkatalogach głównego katalogu nie były indeksowane przez Google. 
Okazuje się, że procedura usuwania niejawnych danych zindeksowanych może trwać 3-4 dni robocze.

Tak więc dobrą praktyką jest stworzenie pliku robots.txt w katalogu głównym domeny z mniej więcej takimi wpisami:

User-Agent: *
Disallow: /Download/
Disallow: /Dokumenty/
...

Wednesday, November 3, 2010

RHEL 6 Beta 2 - moje pierwsze spojrzenie

Nowego RHEL6 można pobrać bez rejestracji z serwera ftp. Ciekawy byłem co się w nim znalazło z projektu Fedora oraz jakie są wersje pakietów w stosunku do Debiana. Instalację przeprowadziłem na VirtualBox.


Zainstalowałem system w opcji Basic Server, więc bez UI. Podczas startu system chwilowo się zaciął, musiałem przełączyć się na inny terminal i zobaczyć co jest grane. Jak to zrobić? Na maszynie wirtualnej należy użyć skrótu "host key"-F1 (Ctrl+Alt+F1). Zaciął się przy przebudowaniu inird.

Jak tu teraz coś zainstalować, yum nie działa! Skorzystałem z porady. Hy, ale my nie mamy neta, musiałem ustawić DNS, eth0 i routing. Wszystko ładnie pięknie, tylko że po restarcie on straci te ustawiania, kolejna porada jak to zapisać.
Teraz instalacja hurtowa:

# yum groupinstall "X Window System" "GNOME Desktop Environment"

Lepiej był to pociągnąć z płyty, ale już trudno.
(parę minut)...

My, a jednak szybciej będzie od nowa zainstalować system z DVD.


Niestety już na samym początku przetestowałem tryb ratunkowy :) - nie przestawiajcie kolejności dysków w VB, bo to "skutkuje" utratą Gruba.

Teraz będę mógł przyjrzeć się systemowi, wielkiemu RHEL. Na pierwszy rzut oka (przy instalacji) zauważyłem, ze jądro jest w wersji 2.6.32, jak w squeeze. Domyślnie system używa LVM i ext4. Poza tym jest bardzo podobny do fedory.

Tuesday, October 19, 2010

Aktualizacja ips_outline.py

Parę dni temu otrzymałem prywatną wiadomość z prośbą o pomoc w ustawianiu mojego IPS-a do poprawnego wykrywania logu:
imapd: LOGIN FAILED, user=***@***, ip=[::ffff:adres_ip]
Skrypt opisałem wcześniej można go pobrać ze strony projektu: http://code.google.com/p/ipsoutline/

Przypomnę składnię:

name;;find1,find2,NULL;;key1,between1,between2;key2,between1,between2;;iptables rule param param;1,2;N;time

W tym przypadku poprawna reguła powinna wyglądać następująco:

imapd_auth;;imapd,LOGIN FAILED,NULL;;ip=[::ffff,:,];;iptables -A INPUT -s param -j ACCEPT;1;1;00:01:00

Kluczem jest ip=[::ffff, a jego wartość zawiera się między :, a ].
Przy określeniu takiej składni na jaw wyszedł pewien błąd, który poprawiłem. Wyjaśnić należy jeszcze, że klucz może mieć dowolną długość, ale ograniczniki szukanej wartości (muszą znajdować się po kluczu) muszą być pojedynczymi znakami (tak zostało to napisane, ale oczywiście można by to zmienić).

Ponadto padł pomysł, aby dodać powiadamianie na e-maila. Pomysł fajny, więc i zrealizowany.
Swój adres e-mail należy wpisać w liniach 178 (dla wpisów istniejących) oraz 191 (dla wpisów nowych). Nie polecam używania powiadomienia dla istniejących wpisów, może to generować bardzo wiele zbędnych wiadomości.

Warto również wspomnieć o pewnej właściwości, o której chyba nie wspominałem we wcześniejszych wpisach. Spójrzmy na regułę ips-a:

sshd;;sshd,Failed password,NULL;;from, , ;port, , ;;iptables -I INPUT 3 -s param -j DROP;1;3;00:01:00


Zaznaczony kod (reguła iptables) będzie wykonana tak jak każda inna w terminalu. Tak więc nic nie stoi na przeszkodzie, aby na jej końcu dodać operator && i dopisać zupełnie inną komendę, która zostanie wykonana, bądź też wykonać cały skrypt.

Saturday, September 4, 2010

Porównanie systemów: biblioteki .so czy .dll?

Temat bibliotek jest na co dzień mało spotykany, po prostu system, a raczej aplikacje ich potrzebują aby działać. Jednak biblioteki dynamicznie ładowane (.dll) czy współdzielone (.so) pozornie decydują o czymś więcej, niż tylko o tym, czy program ich wymagający się uruchomi. Okazuje się, że od tych założeń zależy w bardzo dużej mierze ocena użytkowników co do całego system. Dlaczego? Ponieważ jedne z modeli stwarza problemy dla użytkownika, a drugi nie. W takim razie po co ktoś wybrał te pierwsze rozwiązanie? Wszystkie odpowiedzi na te pytania zmierzają do ostatnio bardzo popularnego tematu związanego z bezpieczeństwem w systemach Windows. Temat ten jest obecnie na tyle gorący aby zainteresować nim czytelnika. Jeżeli zastanawiasz się dlaczego GNU/Linux nie może zastąpić systemu Windows, oraz dlaczego na Windows XP bez problemu zainstalujesz najnowsza aplikacje podczas, gdy na pingwinie oznacza to wojnę z bibliotekami, to ten artykuł jest przeznaczony dla Ciebie.

Gorącym tematem, o którym wspomniałem jest „DLL hijacking”, czyli atak na bibliotekę programu, a właściwie jej wykorzystanie do uruchomienia własnego kodu. Zanim omówię ten problem najpierw jednak wrócę do pytania dlaczego Windows jest „lepszy”?. Dla przykładu porównam mechanizm używania bibliotek, z których korzysta aplikacja, np. Firefox na obu systemach, GNU/Linux i Windows XP. Dlaczego starsza wersja systemu? Ponieważ świetnie obrazuje efekt innego podejścia do implementacji mechanizmu bibliotek.

Chyba każdy wie, ze biblioteki te istnieją po to, aby zaoszczędzić pamięć. Gdy potężna aplikacja zostaje uruchomiona, do pamięci ładowany jest przez system operacyjny minimalny kod. Jest to zależne od programistów aplikacji, a nie systemu. Gdy użyjemy którejś z bardziej rozbudowanych funkcji, program załaduje odpowiedni kod jego obsługi, np. czytanie innego formatu plików wideo. Wczytanie to ma postać załadowania pliku .dll lub .so do przestrzeni roboczej programu.

Wróćmy jednak do instalacji Firefoxa 4 na dwóch różnych systemach. Gdy zechcemy zaktualizować swoją przeglądarkę na systemie Windows XP wystarczy że ją ściągniemy, zainstalujemy i to wszystko. Instalacja powiedzie się, mimo iż instalujemy nową aplikacje na systemie, który ma prawie 10 lat. Wszystkie biblioteki wymagane prze całkowicie nową aplikacje zostaną skopiowane do katalogu macierzystego programu i stamtąd zostaną załadowane przez aplikacje.

W przypadku GNU/Linux tak łatwo już nie ma. Możemy ściągnąć najnowsze źródła albo odpowiedni plik binarny i spróbować uruchomić go na naszym systemie. Niestety zakończy się to komunikatem o niespełnionych zależnościach, np. w postaci „Biblioteka X jest w wersji 1.0, wymagana jest wersja 2.0”. Jaki jest tego powód? Właściwie dwa. Pierwszy to model projektowy, który zakłada centralizacje i nie duplikowanie wersji bibliotek zgromadzonych w systemie. Podobnie jest w systemie Windows, z tym, że w nim scentralizowane są tylko biblioteki systemowe, np. gui32.dll, ntdll.dll, user32.dll. Biblioteki używane przez programy nie są związane w żaden sposób z systemem, są one niezależne od niego.

Chyba już wszyscy wiedza w czym problem. Pozostaje teraz pytanie, w takim razie dlaczego w systemach Unix nie przyjęto takiego założenia jak w systemie Windows sokor daje większe możliwości? Odpowiedzią na to pytanie jest między innymi obecnie popularna podatność występująca w systemach z rodziny Windows zwana DLL hijacking. Jak już wspomniałem, podatność ta polega na podkładaniu własnego kodu, który zostanie załadowany do pamięci przez jakiś program. Możliwe jest to, ponieważ biblioteki te nie są chronione przez mechanizmy obronne systemu, np. takie jak prawa dostępu. Dlaczego? Ponieważ nie są jego częścią. Czy separowanie bibliotek naprawdę jest dużym problemem? Okazuje się, ze tak. Gdy w systemie Unix zostanie odkryta podatność wszystkie aplikacje używające podatnej biblioteki stają się zagrożone, ale aktualizacja systemowa biblioteki naprawia ten problem hurtowo. W przypadku systemu Windows nie ma takiej możliwości załatania biblioteki, z których korzysta np. Firefox. Obecnie poprawianie takich błędów wymaga indywidualnego nakładania łatek dla każdej aplikacji z osobna, co jak wiadomo w dużym środowisku jest niewygodnym rozwiązaniem.

Dlatego podejście użyte w systemach Unix ma sens i przetrwało do dziś, opierając się modzie na nowe oprogramowanie. Jest to również jeden z głównych powodów projektowych, z których używanie aplikacji biurkowych jest w pewnym sensie ograniczone. Projektanci systemu Unix postawili na centralizację oraz bezpieczeństwo mechanizmu ładowanych bibliotek. Projektanci systemu Windows postawili na umożliwienie użytkownikowi zarządzanie bibliotekami, w celu bezproblemowej aktualizacji aplikacji. Tylko, czy powierzenie użytkownikowi takiego zadania jest rozsądną decyzją? Z punktu widzenia projektantów systemu i celów jakie mieli osiągnąć z pewności tak. Świadczy o tym popularyzacja tego systemu jako platformy systemowej dla większości aplikacji.

Różnica z podejść projektowych wynika jeszcze z samego modelu w jakim jest rozprowadzany kod. W przypadku bibliotek otwartych zarządzanie nimi przez system nie stanowi problemu. Inaczej ma się sprawa do oprogramowania komercyjnego, którego twórcy nie chcą aby ktoś lub coś kontrolowało ich kod.

Tuesday, August 17, 2010

To nic osobistego, po prostu Debian jest lepszy: lp_solve vs Excel Solver

Programowanie liniowe może nie jest najczęściej zlecanym zadaniem dla komputera domowego ale w niektórych przypadkach taki sposób rozwiązywania problemów jest jedynym optymalnym. Dla osób, które nie miały okazji jeszcze zapoznać się z programowaniem liniowym wspomnę, iż jest to dziedzina nauki matematycznej zwana badaniami operacyjnymi. Dzięki tym metodom opisywania problemów możemy uzyskać optymalne rozwiązanie jakiegoś problemu. Jednym z przykładów takich rozwiązań są programy, które po wskazaniu gdzie i skąd chcesz dojechać ustalają najkrótsza z możliwych dróg. Wtedy minimalizujesz zużycie kosztów podróży – prawda że życiowe.

W swojej pracy zdarzały mi się takie zadania, których rozwiązywanie na piechotę krótko mówiąc byłoby bardzo dręczące. Tak więc, gdy mamy do czynienia z rozdysponowaniem ograniczonych zasobów do realizacji ustalonego zadania mamy również do czynienie z zadaniem dążącym do maksymalizacji bądź minimalizacji. Zamiast na kartce papieru rozpisywać wszystkie możliwe kombinacje i szukać tej, która spełni nasze oczekiwania lepiej użyć do tego urządzenia, które zostało stworzone do analizy danych – czyli komputera.

Jeszcze przed potrzebą korzystania z takiego narzędzia wiedziałem o istnieniu dodatku do programu Excel zwanym Solver. Rok temu przyszedł czas jego wykorzystania. Miałem okazje go przetestować, w początkowej fazie byłem z niego zadowolony. Jednak gdy zacząłem zlecać mu coraz bardziej skomplikowane zadania ten albo w ogóle nie generował wyników albo były one nierealne! Nie miałem wyjścia, szybko zacząłem szukać innego narządzi i oczywiście darmowego. Znalazłem lp_solve, który używał najpopularniejszych formatów zapisu zadania liniowego między innymi MPS, którego zazwyczaj uczy się na studiach. Ja osobiście go nie lubiłem ale dostępny był również prostszy format zwany lp. lp_solve potrafił sprostać wymaganiom, z którymi Solver z Excela sobie po prostu nie radził. Problem, na podstawie którego stwierdziłem iż lp_solve jest o wiele lepszy od rozwiązania zaimplementowanego w pakiecie Office był bardziej złożony niż ten, który przedstawię tu jak przykład. Mimo to, że tu zaprezentuje to na prostszym przykładzie to Excel Solver z nim również sobie nie radzi.
Przejdźmy do faktów i przetestujmy oba narzędzia na przykładowym problemie. Problem, który do tego celu wymyśliłem jest tej samej klasy, z którym ostatnio miałem naprawdę do czynienia.

Załóżmy, że mamy ośrodek szkolenia kierowców, w którym mamy trzy sale do prowadzenia zajęć teoretycznych. Uzbieraliśmy aż 4 grupy, ale wszystkie mogą tylko w tym samym czasie odbywać zajęcia, bo np. wszystkim pasuje po pracy. Mamy więc o jedną salę za mało aby wszystkie grupy mogły się pomieścić na raz. Ponadto wszystkie grupy muszą łącznie odbyć 15 spotkań. Dni, w których te spotkania mogą się odbywać jest 20. Oczekuje się takiego rozplanowania harmonogramu dla tych grup aby dla każdej z nich zajęcia były równomiernie rozłożone w ciągu tych 20 dni.

Zaczniemy od Excela, stworzymy tabelkę, gdzie kolumny będą odzwierciedlać grupy, a wiersze kolejne dni, w których mogą odbywać się spotkania. Jeżeli w komórce położonej na skrzyżowaniu grupy oraz konkretnego dnia znajduje się 1 to znaczy, że grupa w tym czasie ma zajęcia, w przeciwnym wypadku będzie to 0. Tabelka będzie wypełniana danym w formie binarnej (0,1).
Każdej komórce w polu tabeli przydzielona jest zmienna, w tym przypadku oznaczona po prostu adresem komórki. Jak widzimy nasza funkcja optymalizująca ma postać prostego dodawania wszystkich pól. Dzięki temu, że nasz program będzie operował na liczbach binarnych nie będziemy musieli tworzyć warunków nakazujących aby liczby były większe lub równe zero itp..
W opcjach możemy zauważyć, jaki jest zakres zmienianych komórek (zmiennych), że funkcja ma maksymalizować wynik oraz warunki jakie przy tym wszystkim muszą być spełnione. Zaznaczony warunek mówi dla Solvera, że zmienne ma traktować jak binarne, czyli tylko wartości 0,1. Warunek poniżej wymaga aby w każdym wierszu nie było więcej jedynek niż 3, czyli w każdym dniu mogą pomieścić się tylko 3 grupy. Należy taki warunek założyć dla każdego wiersza oddzielnie. Powyżej zaznaczonej opcji mamy warunki dla kolumn, odpowiadają one wymogowi który mówi, że wszystkich spotkań każda z grup musi odbyć 15.
Na powyższym rysunku widzimy jak zakłada się takie warunki w Excelu. To są podstawowe warunki, więc dajmy już teraz szanse na Excel Solver i każmy rozwiązać mu to zadanie zanim zaczniemy dodawać kolejne warunki.
Proszę bardzo, oto na co stać wspaniały dodatek Solver do Excela. Wstawił same zera. Próbowałem przy innych warunkach – wstawiał same jedynki, przy innych opcjach same czwórki. Jak widzimy Solver sobie po prostu nie poradził z tak trywialnym zadaniem, przecież to by można od biedy na kartce rozpisać. Warto tu wspomnieć, że przygotowanie funkcji oraz warunków zadania było dość pracochłonne, zwłaszcza biorąc pod uwagę, że jest to narzędzie obsługiwane z graficznym interfejsem.

Teraz to zadanie zlecimy dla lp_solve. Jest to narzędzie bez graficznego interfejsu ale bez obaw, w przypadku większych zadań będzie to jego atutem. Zaczynamy edytować plik test.lp, składnia opisana jest pod adresem http://lpsolve.sourceforge.net/5.5/ zakładka „lp file format”.
Zmienne, które były komórkami w Excelu, tu będziemy oznaczać:
- możliwe spotkania grupy A: a1, a2, .. a20
- możliwe spotkania grupy B: b1, b2, .. b20
- możliwe spotkania grupy C: c1, c2, .. c20
- możliwe spotkania grupy D: d1, d2, .. d20
Pierwsze co musimy robimy to funkcje celu, czyli sumujemy wszystkie pola z przedstawianej tabeli po kolumnach. Hy, musimy wypisać osiemdziesiąt razy „a1 +”? Nie. BASH zrobi to za nas:

$ for ((i=1; i<=20; i++)); do echo -n "a$i + "; done && echo a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8 + a9 + a10 + a11 + a12 + a13 + a14 + a15 + a16 + a17 + a18 + a19 + a20 +

Powtarzamy pętle dla zmiennych b, c, d i już mamy funkcje celu:

max: a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8 + a9 + a10 + a11 + a12 + a13 + a14 + a15 + a16 + a17 + a18 + a19 + a20 +  
      b1 + b2 + b3 + b4 + b5 + b6 + b7 + b8 + b9 + b10 + b11 + b12 + b13 + b14 + b15 + b16 + b17 + b18 + b19 + b20 + 
      c1 + c2 + c3 + c4 + c5 + c6 + c7 + c8 + c9 + c10 + c11 + c12 + c13 + c14 + c15 + c16 + c17 + c18 + c19 + c20 + 
      d1 + d2 + d3 + d4 + d5 + d6 + d7 + d8 + d9 + d10 + d11 + d12 + d13 + d14 + d15 + d16 + d17 + d18 + d19 + d20;

teraz ograniczenie dotyczące dostępności tylko 3 sal dla 4 grup (a,b,c,d):
# for ((i=1; i<=20; i++)); do echo "spotkanie_$i: a$i + b$i + c$i + d$i <= 3;"; done spotkanie_1: a1 + b1 + c1 + d1 <= 3; spotkanie_2: a2 + b2 + c2 + d2 <= 3; spotkanie_3: a3 + b3 + c3 + d3 <= 3; spotkanie_4: a4 + b4 + c4 + d4 <= 3; spotkanie_5: a5 + b5 + c5 + d5 <= 3; spotkanie_6: a6 + b6 + c6 + d6 <= 3; spotkanie_7: a7 + b7 + c7 + d7 <= 3; spotkanie_8: a8 + b8 + c8 + d8 <= 3; spotkanie_9: a9 + b9 + c9 + d9 <= 3; spotkanie_10: a10 + b10 + c10 + d10 <= 3; spotkanie_11: a11 + b11 + c11 + d11 <= 3; spotkanie_12: a12 + b12 + c12 + d12 <= 3; spotkanie_13: a13 + b13 + c13 + d13 <= 3; spotkanie_14: a14 + b14 + c14 + d14 <= 3; spotkanie_15: a15 + b15 + c15 + d15 <= 3; spotkanie_16: a16 + b16 + c16 + d16 <= 3; spotkanie_17: a17 + b17 + c17 + d17 <= 3; spotkanie_18: a18 + b18 + c18 + d18 <= 3; spotkanie_19: a19 + b19 + c19 + d19 <= 3; spotkanie_20: a20 + b20 + c20 + d20 <= 3;

Wynik polecenie wklejamy do naszego pliku test.lp. Kolejnym warunkiem jest to, że każda z grup w sumie musi odbyć 15 spotkań. Kopiujemy naszą funkcje celu i przerabiamy ją na następującą postać:

grupa_a: a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8 + a9 + a10 + a11 + a12 + a13 + a14 + a15 + a16 + a17 + a18 + a19 + a20 = 15;
grupa_b: b1 + b2 + b3 + b4 + b5 + b6 + b7 + b8 + b9 + b10 + b11 + b12 + b13 + b14 + b15 + b16 + b17 + b18 + b19 + b20 = 15;
grupa_c: c1 + c2 + c3 + c4 + c5 + c6 + c7 + c8 + c9 + c10 + c11 + c12 + c13 + c14 + c15 + c16 + c17 + c18 + c19 + c20 = 15;
grupa_d: d1 + d2 + d3 + d4 + d5 + d6 + d7 + d8 + d9 + d10 + d11 + d12 + d13 + d14 + d15 + d16 + d17 + d18 + d19 + d20 = 15;


Zostało jeszcze poinformować solver, że zmienne są binarne:

$ for ((i=1; i<=20; i++)); do echo -n "a$i, "; done && echo a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20,

Powtarzamy analogicznie dla zmiennych b,c,d i składamy to do następującej postaci:
bin a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, 
     b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18, b19, b20, 
     c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16, c17, c18, c19, c20, 
     d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13, d14, d15, d16, d17, d18, d19, d20;

Dobrze, mamy już wszystko co mieliśmy w Excelu, każmy teraz dla lp_solve rozwiązać problem:
$ lp_solve test.lp  
  
 Value of objective function: 60 
  
 Actual values of the variables: 
 a1                              1 
 a2                              1 
 a3                              1 
 a4                              1 
 a5                              1 
 a6                              1 
 a7                              1 
 a8                              1 
 a9                              1 
 a10                             1 
 a11                             1 
 a12                             1 
 a13                             1 
 a14                             1 
 a15                             1 
 a16                             0 
 a17                             0 
 a18                             0 
 a19                             0 
 a20                             0 
 b1                              1 
 b2                              1 
 b3                              1 
 b4                              1 
 b5                              1 
 b6                              1 
 b7                              1 
 b8                              1 
 b9                              1 
 b10                             1 
 b11                             0 
 b12                             0 
 b13                             0 
 b14                             0 
 b15                             0 
 b16                             1 
 b17                             1 
 b18                             1 
 b19                             1 
 b20                             1 
 c1                              1 
 c2                              1 
 c3                              1 
 c4                              1 
 c5                              1 
 c6                              0 
 c7                              0 
 c8                              0 
 c9                              0 
 c10                             0 
 c11                             1 
 c12                             1 
 c13                             1 
 c14                             1 
 c15                             1 
 c16                             1 
 c17                             1 
 c18                             1 
 c19                             1 
 c20                             1 
 d1                              0 
 d2                              0 
 d3                              0 
 d4                              0 
 d5                              0 
 d6                              1 
 d7                              1 
 d8                              1 
 d9                              1 
 d10                             1 
 d11                             1 
 d12                             1 
 d13                             1 
 d14                             1 
 d15                             1 
 d16                             1 
 d17                             1 
 d18                             1 
 d19                             1 
 d20                             1

Ba, jak widać jednak się da! Jeszcze jedna rzecz mi się nie podoba, grupie A również ;) - mają zajęcia dzień w dzień a potem na koniec nie mają ich wcale przed egzaminem. To niepożądana sytuacja, zajęcia muszą być dla wszystkich grup równomiernie rozłożone o ile to możliwe. Można znaleźć wiele sposobów na to i uzyskać mniej lub bardziej pożądany wynik. Po przetestowaniu wielu pomysłów doszedłem do wniosku, że warunek nakazujący wstawić jedną przerwę na każde 4 kolejne dni dla danej grupy jest wystarczający. Oczywiście warunki wygenerujemy przy użyciu pętli:

$ for ((i=1,j=2,h=3,g=4; g<=20; i=i+4,j=j+4,h=h+4,g=g+4)); do echo "odstep_dla_a: a$i + a$j + a$h + a$g >= 3;"; done

Ponownie zmieniamy w pętli wszystkie wystąpienia zmiennej a na b, c, d i wyniki wklejamy do pliku. Poniżej przedstawiam cały plik test.lp:
// funkcja celu
//for ((i=1; i<=20; i++)); do echo -n "a$i + "; done && echo
max: a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8 + a9 + a10 + a11 + a12 + a13 + a14 + a15 + a16 + a17 + a18 + a19 + a20 + 
     b1 + b2 + b3 + b4 + b5 + b6 + b7 + b8 + b9 + b10 + b11 + b12 + b13 + b14 + b15 + b16 + b17 + b18 + b19 + b20 +
     c1 + c2 + c3 + c4 + c5 + c6 + c7 + c8 + c9 + c10 + c11 + c12 + c13 + c14 + c15 + c16 + c17 + c18 + c19 + c20 +
     d1 + d2 + d3 + d4 + d5 + d6 + d7 + d8 + d9 + d10 + d11 + d12 + d13 + d14 + d15 + d16 + d17 + d18 + d19 + d20;

// podczas każdego spotkania dostepnych mamy tylko 3 sale
// for ((i=1; i<=20; i++)); do echo "spotkanie_$i: a$i + b$i + c$i + d$i <= 3;"; done
spotkanie_1: a1 + b1 + c1 + d1 <= 3;
spotkanie_2: a2 + b2 + c2 + d2 <= 3;
spotkanie_3: a3 + b3 + c3 + d3 <= 3;
spotkanie_4: a4 + b4 + c4 + d4 <= 3;
spotkanie_5: a5 + b5 + c5 + d5 <= 3;
spotkanie_6: a6 + b6 + c6 + d6 <= 3;
spotkanie_7: a7 + b7 + c7 + d7 <= 3;
spotkanie_8: a8 + b8 + c8 + d8 <= 3;
spotkanie_9: a9 + b9 + c9 + d9 <= 3;
spotkanie_10: a10 + b10 + c10 + d10 <= 3;
spotkanie_11: a11 + b11 + c11 + d11 <= 3;
spotkanie_12: a12 + b12 + c12 + d12 <= 3;
spotkanie_13: a13 + b13 + c13 + d13 <= 3;
spotkanie_14: a14 + b14 + c14 + d14 <= 3;
spotkanie_15: a15 + b15 + c15 + d15 <= 3;
spotkanie_16: a16 + b16 + c16 + d16 <= 3;
spotkanie_17: a17 + b17 + c17 + d17 <= 3;
spotkanie_18: a18 + b18 + c18 + d18 <= 3;
spotkanie_19: a19 + b19 + c19 + d19 <= 3;
spotkanie_20: a20 + b20 + c20 + d20 <= 3;

// każda grupa w sumie musi odbyć 16 spotkań
grupa_a: a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8 + a9 + a10 + a11 + a12 + a13 + a14 + a15 + a16 + a17 + a18 + a19 + a20 = 15;
grupa_b: b1 + b2 + b3 + b4 + b5 + b6 + b7 + b8 + b9 + b10 + b11 + b12 + b13 + b14 + b15 + b16 + b17 + b18 + b19 + b20 = 15;
grupa_c: c1 + c2 + c3 + c4 + c5 + c6 + c7 + c8 + c9 + c10 + c11 + c12 + c13 + c14 + c15 + c16 + c17 + c18 + c19 + c20 = 15;
grupa_d: d1 + d2 + d3 + d4 + d5 + d6 + d7 + d8 + d9 + d10 + d11 + d12 + d13 + d14 + d15 + d16 + d17 + d18 + d19 + d20 = 15;

// przerwy
// for ((i=1,j=2,h=3,g=4; g<=20; i=i+4,j=j+4,h=h+4,g=g+4)); do echo "odstep_dla_d: d$i + d$j + d$h + d$g >= 3;"; done
odstep_dla_a: a1 + a2 + a3 + a4 >= 3;
odstep_dla_a: a5 + a6 + a7 + a8 >= 3;
odstep_dla_a: a9 + a10 + a11 + a12 >= 3;
odstep_dla_a: a13 + a14 + a15 + a16 >= 3;
odstep_dla_a: a17 + a18 + a19 + a20 >= 3;

odstep_dla_b: b1 + b2 + b3 + b4 >= 3;
odstep_dla_b: b5 + b6 + b7 + b8 >= 3;
odstep_dla_b: b9 + b10 + b11 + b12 >= 3;
odstep_dla_b: b13 + b14 + b15 + b16 >= 3;
odstep_dla_b: b17 + b18 + b19 + b20 >= 3;

odstep_dla_c: c1 + c2 + c3 + c4 >= 3;
odstep_dla_c: c5 + c6 + c7 + c8 >= 3;
odstep_dla_c: c9 + c10 + c11 + c12 >= 3;
odstep_dla_c: c13 + c14 + c15 + c16 >= 3;
odstep_dla_c: c17 + c18 + c19 + c20 >= 3;

odstep_dla_d: d1 + d2 + d3 + d4 >= 3;
odstep_dla_d: d5 + d6 + d7 + d8 >= 3;
odstep_dla_d: d9 + d10 + d11 + d12 >= 3;
odstep_dla_d: d13 + d14 + d15 + d16 >= 3;
odstep_dla_d: d17 + d18 + d19 + d20 >= 3;

// zmienne sa typu binarnego
// for ((i=1; i<=20; i++)); do echo -n "a$i, "; done && echo
bin a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20,
    b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18, b19, b20,
    c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16, c17, c18, c19, c20,
    d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13, d14, d15, d16, d17, d18, d19, d20;

Po wygenerowaniu rozwiązania skopiowałem dane do pliku tekstowego w systemie Windows. Z Excela importowałem dane, oddzielając je spacjami w oddzielne kolumny. Po skopiowaniu wyników poszczególnych zmiennych do wcześniej utworzonej tabelki otrzymałem rozwiązanie.

Wydaje mi się, że poprzez użycie pętli powłoki do generowania warunków (już po ich opracowaniu) tekstowe narzędzie zostawiło w tyle graficznego giganta. Biorąc pod uwagę, to że Office jest dominującym produktem w swojej kategorii a Solver istnieje w nim już od bardzo dawna to wstyd, że nie poradził sobie z tak prostym zadaniem.

lp_solve potrafi wiele więcej niż pokazałem, rozwiązywałem na nim o wiele bardziej skomplikowane zadania i zawsze, o ile to logicznie było możliwe generował wyniki.