Po updatu na CyanogenMod 4.2.7 se mi v trayi telefonu opět
objevila nepříjemná ikonka signalizující nedostatek místa v interní
paměti telefonu. Na tom by nebylo zas až tak nic špatného, kdyby mi
z nenadání přestala fungovat synchronizace a download aplikací v Marketu.
Obětoval jsem celé odpoledne na to, abych zjistil, že onu nefunkční
synchronizaci má za následek právě nedostatek místa, na který jsem byl
non-stop upozorňován. Takže co s tím?
Apps2sd
Řešení integrované přímo do CyanogenModu spočívající
v jednoduchém shell skriptu, který pří bootu telefonu na základě
předpokládané existence druhého diskového
oddílu na SD kartě tento oddíl použije a přesune na něj veškeré
nainstalované aplikace a data s nimi související. Nevýhodou je předchozí
úprava karty pomocí nástrojů na dělení disku a naformátování na
linuxový filesystém ext2/3/4. Za odměnu ale dostanete přenositelné aplikace
i s nastavením.
Skript pracuje zcela automaticky, tudíž stačí jen mít připravenou
partition.
Mé vlastní řešení
Přišel jsem na něj doslova metodou pokus-omyl. Při šťourání se
v datové partition jsem si všiml adresáře /data/dalvik-cache,
který zabíral úctyhodných 40 MB a tím pádem byl ihned nominován za
největšího žrouta místa. Tento adresář slouží běhovému prostředí
Dalvik VM jako cache optimalizovaného bytekódu, jinými slovy jako
úložiště překompilovaných a upravených binárek používaných aplikací.
Do detailu Dalvik buduje strom závislostí jednotlivých tříd (funkčních
celků aplikace) tak, aby nainstalovaná aplikace běžela co možná nejlépe.
Tento strom je regenerován při každé aktualizaci ROM, proto první boot po
updatu trvá i několik minut.
Dalším zajímavým místem je adresář /cache, který je
stále téměř prázdný. V dosavadním Androidu je použit výhradně jako
dočasné úložiště pro stahované aplikace v Marketu, jenže v poměru
velikosti celé parititon (~60 MB) a jednoho stahovaného balíčku (řádově
jednotky MB) je až škoda toto místo nevyužít. Proto jsem si napsal krátký
skript, který dalvik-cache přesune do tohoto volného
prostoru.
#!/system/bin/sh
# move dalvik cache from internal data memory to /cache
if [ ! -d /cache/dalvik-cache ];
then
mkdir /cache/dalvik-cache
fi
busybox chown 1000:1000 /cache/dalvik-cache
busybox chmod 771 /cache/dalvik-cache
if [ -d /data/dalvik-cache ] && [ ! -h /data/dalvik-cache ];
then
busybox cp -a /data/dalvik-cache/* /cache/dalvik-cache
busybox rm -f /data/dalvik-cache/*
# bind mount dalvik-cache
busybox mount -o bind /cache/dalvik-cache /data/dalvik-cache
Tento skript stačí umístit např. jako /etc/init.d/06custom.
Toho můžeme efektivně docílit vytvořením souboru na SD kartě a
následným zkopírováním do systému těmito příkazy:
mount -o remount,rw /system
cp /sdcard/06custom /etc/init.d
chmod 755 /etc/init.d/06custom
… a pokračovat rebootem telefonu.
Pokud jste vše udělali správně, máte nyní v interní paměti (aka
/data) okolo 40 MB volného místa. Povedlo se!
Pozn.: Uvedený postup je třeba opakovat po každém updatu ROM, kdy
dojde k přepsání systémové partition a tedy i vlastního skriptu.
Doporučuji proto skript na SD kartě ponechat a před bootem
do updatlé ROM spustit konzoli a zopakovat příkazy uvedené výše.
Štítky:
Tipy a triky
Trocha teorie
Dlouholetým problémem UNIXově
založených operačních systémů je adresář /tmp sloužící
jako dočasné úložiště pro všechny. Tento adresář má práva nastavená
na rwxrwxrwx, tedy zápis pro kohokoli. Nevýhodou však byl
i absolutní přístup k datům jiných uživatelů, a proto byl zaveden tzv.
sticky bit, který omezuje zápis pouze na soubory a adresáře
vlastněné patřičným uživatelem. Každý tedy může zapisovat nové
soubory, ale jen do těch existujících, které vlastní.
Novou možnost fyzické separace pak nabízí tzv.
polyinstantiation. Každému uživateli namapuje daný adresář na
jiný, pro něj v původní cestě nepřístupný. V případě adresáře
/tmp by se např. jednalo o mapu z /tmp-inst nebo
klidně i /tmp/users. Mechanismus, který polyinstancování
zajišťuje, automaticky vytvoří adresář dle uživatelského jména a jím
nahradí cílový adresář dle mapy.
Ukázka z praxe
Nastavení se provádí v konfiguračním souboru
/etc/security/namespace.conf prostřednictvím PAM modulu
pam_namespace.so:
# /etc/security/namespace.conf
#
# See /usr/share/doc/pam-*/txts/README.pam_namespace for more information.
#
#/tmp /tmp-inst/ both root,adm
#/var/tmp /var/tmp/tmp-inst/ both root,adm
#$HOME $HOME/$USER.inst/inst- context
/tmp /tmp/users/ user root
Zakomentované příklady zahrnují polyinstancing /tmp,
/var/tmp (který osobně doporučuji nastavit jako symbolický
odkaz do /tmp) a domovského adresáře uživatele. Důležitý
parametr je typ, který zpravidla může být:
- user (na základě uživatelského jména)
- context (na základě bezpečnostního kontextu, pouze
SElinux)
- both (obojí)
Poslední čtvrtý parametr udává výjimky. V mém případě bude na
základě uživatelského jména namapován adresář /tmp z
/tmp/users s výjimkou uživatele root.
Jak na to
Nejprve vytvoříme adresář, ze kterého budeme mapovat, v mém případě
/tmp/users. Tomuto adresáři nastavíme z bezpečnostních
důvodů nulová práva a zadáme mapu
v konfiguračním souboru /etc/security/namespace.conf.
Nyní je třeba pečlivě zvážit nastavení modulů PAM. Po zkušenostech
z praxe nedoporučuji spouštět PAM modul ve
všech případech autentizace (soubor
/etc/pam.d/common-session, způsobí to více problémů než
užitku. Polyinstancování jsem proto zúžil na dva jediné typy
autentizace – login a sshd.
Ukázka začlenění PAM modulu do souboru s autentizací:
# Enable polyinstantiation
session required pam_namespace.so
Je třeba dát si pozor na to, kam modul umístíme. Existují místa, kdy je
moc brzy a kdy zase moc pozdě – lepší je si to párkrát zkusit a pozorně
sledovat logy (např. auth.log). Pokud se mechanismus aplikuje,
měli bychom po novém přihlášení mít adresář /tmp
v ideálním případě prázdný. Přihlásíme-li se na roota, měli bychom
vidět /tmp/users a v něm adresář s naším a případně
dalšími uživatelskými jmény. Máme hotovo.
Osobně mám přímé přihlášení na root uživatele zakázané a
přistupuji přes sudo. Bohužel v tomto případě se mi nepodařilo sudo
nijak donutit vyvolat čistý login (nejspíše proto, že sudo je spouštěno
z shellu, na který již bylo polyinstacování aplikované), takže ani po
přihlášení na roota mi /tmp neodhalil žádná svá tajemství.
Pokud nedejbože nutně potřebuji vidět obsah neořezaného /tmp,
obejdu bezpečnostní mechanismus mountnutím root partition např. do
/mnt a podívám se tam :)
Štítky:
Tipy a triky
Awstats je skript v Perlu, který zpracovává logy (nejen) webserveru a ve
výsledku vyplivne pěknou statistiku
přístupů. Dnes si ukážeme, jak Awstats lehce a efektivně nastavit.
Awstats
Standardní konfigurace Awstats se nachází v adresáři
/etc/awstats, kde je i ukázkový soubor
awstats.conf. Jelikož konfigurace podporuje funkci
Include, využijeme tento soubor jako základ pro naše vlastní
konfigurace.
Vytvoříme nový soubor awstats.domain.tld.conf:
# hlavní config, nejprve jej vhodně nastavíme (formát logů, pluginy atd.)
Include "/etc/awstats/awstats.conf"
# log soubor Apache (v mém případě)
LogFile="/home/www/logs/domain.tld.log"
# doména, povinný parametr, který se spíše uplatní při použití spojeného logu (o tom později)
SiteDomain="domain.tld"
# aliasy pro SiteDomain, doporučuji tento formát
HostAliases="REGEX[domain\.tld$]"
Apache 2
Nastavíme Apache, aby logoval. Direktiva CustomLog se může
nacházet uvnitř i vně direktivy <VirtualHost>, podle
toho, co všechno chceme logovat.
Jeden log pro každou doménu
Direktivu přidáme do odpovídajícího VirtualHosta:
<VirtualHost *:80>
ServerName domain.tld
...
CustomLog /home/www/logs/domain.tld.log combined
</VirtualHost>
Jeden log pro celý server
Přidáme mimo VirtualHost:
# vlastní formát logu, prefixem je jméno VirtualHosta
LogFormat "%{Host}i %h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" cplus
CustomLog /home/www/logs/server.log cplus
V případě této konfigurace musíme Awstats říct o novém formátu
logu, což provedeme přímo v konfiguraci awstats.conf úpravou
řádky LogFormat:
LogFormat="%virtualname %host %other %logname %time1 %methodurl %code %bytesd %refererquot %uaquot"
Další nastavení
Alias /awstats-icon/ /usr/share/awstats/icon/
<Directory /usr/share/awstats/icon>
Options None
AllowOverride None
Order allow,deny
Allow from all
</Directory>
Namapuje adresář s obrázky Awstats do URL adresy
/awstats-icon/.
Zpracování
K periodické aktualizaci dat nastavíme cron (pokud již tak distributor
neudělal):
*/15 * * * * root /home/bin/awstats_updateall.pl now --excludeconf=awstats.conf > /dev/null 2>&1
Skript awstats_updateall.pl a dalších pár šikovných
skriptů nalezneme v adresáři /usr/share/doc/awstats/examples.
Tento skript zpracovává všechny konfigurační soubory,
takže aktualizuje všechny domény.
Máme-li správně nastavené CGI, což ve výchozí konfiguraci obvykle
bývá, nalezneme webový frontend awstats na adrese
http:domain.tld/cgi-bin/awstats.pl?config=domain.tld//. Kromě
řešení na bázi Perl frontendu a awstats_updateall.pl skriptu je
však možné využít awstats_buildstaticpages.pl, který přímo
vygeneruje HTML soubory do adresáře zadaného v parametru. Toto řešení
převážně využívám na webhostingách, má však tu nevýhodu, že není
možné zobrazit statistiky za jiná období a zákazník vždy vidí pouze
aktuální měsíc.
Vylepšení
Napsal jsem rewrite, který podstatně zkrášluje adresu a parametr
předávaný skriptu /cgi-bin/awstats.pl na webu:
RewriteEngine On
RewriteRule ^/?([^/]+)/$ /cgi-bin/awstats.pl?config=$1 [PT,L,QSA]
Výše zmíněný kód je vhodné aplikovat na určitou subdoménu, např.
stats. Statistiky pak nalezneme na adrese
http://stats.anotherdomain.tld/domain.tld/.
Statistiky subdomén
Awstats se samozřejmě dá nastavit tak, aby u určité domény logoval
i každou subdoménu. Tento přístup kombinuje a upravuje formáty logů
popsané výše. Každá subdoména musí mít svůj VirtualHost a definovaný
log, v optimálním případě budou logy pro každou doménu zvlášť a
v nich spojené logy subdomén s upraveným formátem (viz. jeden log pro celý server).
Štítky:
Řešení problému
Standardní chybové hlášky v Apachi jsou z informativního charakteru
naprosto dostačující, avšak z toho estetického nikoli. Kdekoho posléze
napadne, jak tyto chybové hlášky přenastavit. Na tom samozřejmě není nic
těžkého, stačí vhodná úprava do souboru .htaccess typu:
ErrorDocument 404 /neexistuje.html
Mít pro každý kód chyby jinou stránku je pak zbytečně komplikované.
Naštěstí existuje řešení, jak různé kódy efektivně zpracovat na
úrovni PHP. Základní zjednodušené nastavení souboru .htaccess
pak bude vypadat obdobně:
ErrorDocument 400 http://400.dragonjake.net
# speciální hack pro 401 Authorization Required
Alias /401/ /var/www/html/
ErrorDocument 401 /401/index.php
ErrorDocument 403 http://403.dragonjake.net
ErrorDocument 404 http://404.dragonjake.net
ErrorDocument 500 http://500.dragonjake.net
Ve svém řešení využívám externí http:// adresy pro
jednotlivé kódy zvlášť, v Apachi nastavené zhruba takto:
<VirtualHost *:80>
ServerName dragonjake.net
ServerAlias 400.dragonjake.net 403.dragonjake.net 404.dragonjake.net 500.dragonjake.net
DocumentRoot /var/www/html
</VirtualHost>
V adresáři /var/www/html se nachází nejočekávanější
soubor, skript index.php, který chybové kódy zpracovává.
Klíčová je superglobální proměnná
$_SERVER['REDIRECT_STATUS'], která vrací HTTP stavový kód,
podle kterého následně skript rozlišuje typ chyby.
Bohužel pro chybu 500 nevrátí proměnná z nepochopitelných důvodů
nic. Skript používám i jako výchozí lokaci pro požadavky, které
neodchytne žádný <VirtualHost>, např. když někdo zadá
přímo IP adresu do prohlížeče. Zjištění chyby jsem tedy oprasil díky URL přes
regulární výraz.
if (empty($code)) {
preg_match('~(\d+)\.dragonjake\.net$~', $_SERVER['HTTP_HOST'], $m);
$code = $m[1];
}
Úmyslně ve své implementaci nepoužívám žádný výpis URL adresy,
která se běžně u podobných chybových hlášení objevuje. Zvědavcům
napovím, že se jedná o proměnné $_SERVER['REQUEST_URI'] a
$_SERVER['REDIRECT_URL']. Na závěr přikládám celý kód
„chybového“ skriptu.
if (empty($code)) {
preg_match('~(\d+)\.dragonjake\.net$~', $_SERVER['HTTP_HOST'], $m);
$code = $m[1];
}
switch ($code) {
case 400:
$h = '400 Bad Request';
$t = 'Err... what did you say?!';
break;
case 401:
$h = '401 Authorization Required';
$t = 'You haven\'t given me a gold!';
break;
case 403:
$h = '403 Forbidden';
$t = 'Step back before you get eaten!';
break;
case 404:
$h = '404 File Not Found';
$t = 'Are you looking for something?!';
break;
case 500:
$h = '500 Internal Server Error';
$t = 'Whops! Something got wrong!';
break;
default:
$h = '';
$t = 'Server is up and running.';
}
if (!empty($h))
header($_SERVER['SERVER_PROTOCOL'] . ' ' . $h, TRUE, $code);
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xml:lang="cs" lang="cs" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<meta http-equiv="content-language" content="cs" />
<meta name="author" content="Dragon Jake; e-mail: admin@dragonjake.net" />
<meta name="copyright" content="© 2006 Dragon Jake" />
<meta name="robots" content="noindex,nofollow" />
<link rel="stylesheet" type="text/css" href="/401/main.css" />
<link rel="shortcut icon" type="image/x-icon" href="/401/favicon.ico" />
<title><?php if (!empty($h)) echo $h . ' | '; ?>Dragon's Server</title>
</head>
<body>
<div id="logo"></div>
<h1><?php echo htmlspecialchars($t); ?></h1>
<a href="http://www.dragonjake.net">www.dragonjake.net</a>
</body>
</html>
Štítky:
Tipy a triky
Založil jsem blog, skutečně blog! Další? A proč?
Proč?
To je dobrá otázka a těžko se na ni odpovídá. Již delší dobu je můj
prvotní dračí web docela
mrtvý, psaní dalších článků do něj je spíše
utrpením. Může za to především zastaralost kódu a jeho nejistá
funkčnost, což má za následek výrazné nepohodlí něco rychle napsat.
Když jsem tedy psal článek, znamenalo to minimálně 2 hodiny úmorné
práce, kterou jsem raději vykonával předem do textového souboru, včetně
formátování. Nakonec to dopadlo tak, že byl článek 10× delší, než jsem
původně zamýšlel.
Cíle tohoto blogu
Tento blog bude čistě IT. Nebudu zde prezentovat své
názory na dění ve světě, nekonečné pochody mých nesmyslných myšlenek
ani zcela fiktivní a nemožné pokusy o literaturu, které jsem vlastně nikdy
pořádně nezveřejnil 
Naopak by měl blog sloužit jako rychlé know-how pro mě a pro
všechny. Budu se pokoušet o časté aktualizace kratšími blogposty ze
všech možných okruhů v problematice IT, kterým se věnuji.
Textpattern?
Ano. Aniž bych měl v lásce různé Wordpressy, Joomly
či Drupaly, sáhl jsem pro tentokrát po otevřeném CMS.
A teď krátká vsuvka – zadal jsem do Googlu „lehký blog
texy“, protože Texy! je prostě sexy a
bez ní nedokážu normálně psát a Textpattern byl jednoznačně na prvním
místě. Byť byl prezentován jako lehký, po prvním seznámení mi
ani zdaleka lehký nepřijde a proto se nyní vzdávám veškerých pokusů
a přizpůsobení. Jsem rád, že se mi povedlo nastavit rubriky a nějaké
další základní věci jsem strávil dalších několik hodin
přizpůsobením designu, až jsem skončil v úpravách samotného HTML kódu
a přidávání další funkcionality, přesto „to“ ještě není
ono.
V dnešní době bohužel nemám přespříliš času, abych si
naprogramoval vlastní, rychlejší a
jednodušší blogovací systém, přestože jsem obětoval celých 30 minut
vymýšlení základní struktury a funkcionality. Časem bych byť zajímavý
Textpattern samozřejmě rád nahradil vlastním řešením… ale čas je
silně relativní 
Štítky:
Obecně