Edit detail for CGI a omezené prostředí revision 1 of 1

1
Editor: mol
Time: 2009/12/15 19:38:06 GMT+1
Note:

changed:
-
Tento text je napsán pro Linux a Apache.

Představme si, že jsme v situaci, kdy potřebujeme napsat CGI skript přistupující
např. k MySQL databázi, ale i když je na daném počítači nainstalována a
nainstalován je i Python, chybí !MySQLdb a, co je ještě horší, i libmysqlclient.
Administrátorská práva nemáme a admin nám nechce potřebné knihovny nainstalovat. Jak z
toho ven?

Začněme tím jednodušším, tj. !MySQLdb. Využijeme toho, že existuje distribuční
balík pro naši distribuci a někam ho rozbalíme (to umí třeba Midnight Commander;
některé distribuce snad umožňují instalaci balíku do uživatelského adresáře).
Cestu do toho adresáře (tj. toho, co obsahuje např. _mysql.so) pak jednoduše
přidáme na úplný začátek našeho CGI skriptu do proměnné sys.path. Příklad
testovacího skriptu::

	#!/usr/bin/python
	# -*- coding: UTF-8 -*-

	import cgi

	import sys     # v mysql_python mame MySQLdb
	sys.path.append('/home/~username/mysql_python') 

	import MySQLdb

	print 'Content-Type: text/html; charset=UTF-8\n'
	print '<h1>Hello</h1>'

	conn = MySQLdb.connect(db=' ... ', passwd=' ... ')
	conn.set_character_set('utf8')
	c = conn.cursor()
	c.execute('select * from clovek')
	for row in c.fetchall():
	    print row[1], '<br>'
	c.close()

	print '<hr>'
	form = cgi.FieldStorage()
	for i in form:
		print i, form[i].value, '<br>'

Druhý problém je složitější. Podobně jako v prvním případě stáhneme a rozbalíme
balíček s libmysqlclient, ale potřebujeme, aby v době spuštění skriptu byl
adresář s touto knihovnou v LD_LIBRARY_PATH. Apache má sice direktivu !SetEnv,
ale na tu je v .htaccess už příliš pozdě. Řešením může být druhý skript, který
má název prvního skriptu jako parametr. Ten nastaví LD_LIBRARY_PATH a pak spustí
náš skript. Příklad takového skriptu::

	#!/usr/bin/python

	from os import environ

	import cgitb; cgitb.enable()

	def print_error(msg):
	    import sys
	    print 'Content-Type: text/html\n'
	    print '<h1>Error</h1>'
	    print '<xmp style="color:red">%s</xmp>' % msg
	    sys.exit()

	filename = None
	method = environ['REQUEST_METHOD']

	if   method == 'GET':
		import cgi
		data = cgi.FieldStorage()
		if data.has_key('script'):
			filename = data['script'].value
	elif method == 'POST':
		for pair in environ['QUERY_STRING'].split('&'):
			key, value = pair.split('=')
			if key == 'script':
				filename = value
				break

	if filename:
	    import re
	    if re.match(r'^\w+$', filename):
		import os
		filename += '.py'
		if os.path.exists(filename):
		    from subprocess import Popen, PIPE
		    # podle toho, kde mame libmysqlclient
		    environ["LD_LIBRARY_PATH"] = "/home/~username/lib"
		    p = Popen(('/usr/bin/python', filename), env=environ, stderr=PIPE)
		    if p.wait() <> 0:
			print_error(p.stderr.read())
		else:
		    print_error('Script does not exists!')
	    else:
		print_error('Script filename must be [a-zA-Z0-9_]+ !')
	else:
	    print 'Location: http:// .... \n'

To sice už funguje, ale pokud už je aplikace vyvinutá, asi se nám nebude chtít
měnit všechna URL z neco.py na run.py?script=neco . To lze vyřešit pomocí modulu
rewrite. Příklad .htaccess::

	AddHandler cgi-script .py .cgi
	RewriteEngine On
	RewriteCond %{QUERY_STRING} ^(.*)$
	RewriteRule ^([^run]+?)\.py$ /adresa_nasich_stranek/cgi-bin/run.py?script=$1&%1 [L]

P.S. Nenapadl mě žádný výstižný text této stránky, tak pokud vás políbí můza neváhejte a přejmenujte.

Tento text je napsán pro Linux a Apache.

Představme si, že jsme v situaci, kdy potřebujeme napsat CGI skript přistupující např. k MySQL databázi, ale i když je na daném počítači nainstalována a nainstalován je i Python, chybí MySQLdb a, co je ještě horší, i libmysqlclient. Administrátorská práva nemáme a admin nám nechce potřebné knihovny nainstalovat. Jak z toho ven?

Začněme tím jednodušším, tj. MySQLdb. Využijeme toho, že existuje distribuční balík pro naši distribuci a někam ho rozbalíme (to umí třeba Midnight Commander; některé distribuce snad umožňují instalaci balíku do uživatelského adresáře). Cestu do toho adresáře (tj. toho, co obsahuje např. _mysql.so) pak jednoduše přidáme na úplný začátek našeho CGI skriptu do proměnné sys.path. Příklad testovacího skriptu:

#!/usr/bin/python
# -*- coding: UTF-8 -*-

import cgi

import sys     # v mysql_python mame MySQLdb
sys.path.append('/home/~username/mysql_python')

import MySQLdb

print 'Content-Type: text/html; charset=UTF-8\n'
print '<h1>Hello</h1>'

conn = MySQLdb.connect(db=' ... ', passwd=' ... ')
conn.set_character_set('utf8')
c = conn.cursor()
c.execute('select * from clovek')
for row in c.fetchall():
    print row[1], '<br>'
c.close()

print '<hr>'
form = cgi.FieldStorage()
for i in form:
        print i, form[i].value, '<br>'

Druhý problém je složitější. Podobně jako v prvním případě stáhneme a rozbalíme balíček s libmysqlclient, ale potřebujeme, aby v době spuštění skriptu byl adresář s touto knihovnou v LD_LIBRARY_PATH. Apache má sice direktivu SetEnv, ale na tu je v .htaccess už příliš pozdě. Řešením může být druhý skript, který má název prvního skriptu jako parametr. Ten nastaví LD_LIBRARY_PATH a pak spustí náš skript. Příklad takového skriptu:

#!/usr/bin/python

from os import environ

import cgitb; cgitb.enable()

def print_error(msg):
    import sys
    print 'Content-Type: text/html\n'
    print '<h1>Error</h1>'
    print '<xmp style="color:red">%s</xmp>' % msg
    sys.exit()

filename = None
method = environ['REQUEST_METHOD']

if   method == 'GET':
        import cgi
        data = cgi.FieldStorage()
        if data.has_key('script'):
                filename = data['script'].value
elif method == 'POST':
        for pair in environ['QUERY_STRING'].split('&'):
                key, value = pair.split('=')
                if key == 'script':
                        filename = value
                        break

if filename:
    import re
    if re.match(r'^\w+$', filename):
        import os
        filename += '.py'
        if os.path.exists(filename):
            from subprocess import Popen, PIPE
            # podle toho, kde mame libmysqlclient
            environ["LD_LIBRARY_PATH"] = "/home/~username/lib"
            p = Popen(('/usr/bin/python', filename), env=environ, stderr=PIPE)
            if p.wait() <> 0:
                print_error(p.stderr.read())
        else:
            print_error('Script does not exists!')
    else:
        print_error('Script filename must be [a-zA-Z0-9_]+ !')
else:
    print 'Location: http:// .... \n'

To sice už funguje, ale pokud už je aplikace vyvinutá, asi se nám nebude chtít měnit všechna URL z neco.py na run.py?script=neco . To lze vyřešit pomocí modulu rewrite. Příklad .htaccess:

AddHandler cgi-script .py .cgi
RewriteEngine On
RewriteCond %{QUERY_STRING} ^(.*)$
RewriteRule ^([^run]+?)\.py$ /adresa_nasich_stranek/cgi-bin/run.py?script=$1&%1 [L]

P.S. Nenapadl mě žádný výstižný text této stránky, tak pokud vás políbí můza neváhejte a přejmenujte.