tag:blogger.com,1999:blog-22593748352867056662024-02-21T22:14:54.905+06:00Ordinary En_t_endUnknownnoreply@blogger.comBlogger13125tag:blogger.com,1999:blog-2259374835286705666.post-59394934992510876262013-04-11T02:12:00.002+07:002013-04-12T13:22:54.734+07:00Tips for using docker: remove running containers, make new image, run image, reading logs<p>
<a href="http://www.docker.io/">Docker</a> - in two words this is a manager of LXC containers.
</p>
<p>
But I guess you know more about docker if you are here.
</p>
<p>
<i>Note: I don't know anything about your environment and running processes, so use next commands on your own risk.</i>
</p>
<a name='more'></a>
<h3>
Killing and removing containers</h3>
<p>
First of all if you are experimenting with docker next command for guaranteed killing all running lxc-processes is possible will useful:
</p>
<pre class="bash" name="code">$ ps aux | egrep lxc | egrep -o "root [0-9]{5}" | egrep -o "[0-9]{5}" | xargs sudo kill -9
</pre>
<p>
Soft killing and removing of docker containers
</p>
<pre class="bash" name="code">sudo docker ps -a -q -notrunc | xargs sudo docker rm
</pre>
<p>
Killing and removing of docker containers by specific image name:
</p>
<pre class="bash" name="code">sudo docker ps -a | grep "$IMAGE" | awk '{print $1}' | sudo xargs docker rm
</pre><br />
<h3>
Making new image by copying JDK and some APP directory</h3>
<pre class="bash" name="code">#!/bin/bash
JDK=/usr/lib/jvm/jdk1.7.0 # My app is java-based
APPPATH=$1
IMAGENAME=$2
DOCKERIMAGE=base # base image
# Run docker in daemon mode
(docker -d || echo "Docker daemon already running") &amp;
# Sleep for a few seconds maybe needed here
CONTAINERBASEPATH=/tmp
# Copy all needed directories
tar -c $JDK $APPPATH | docker run -i $DOCKERIMAGE /bin/sh -c \
"tar -C $CONTAINERBASEPATH/ -x ; \
chown -R root:root $CONTAINERBASEPATH ; \
chmod -R ug=rwx $CONTAINERBASEPATH ; \
chmod -R o=rx $CONTAINERBASEPATH"
# Find current running container id
CONTAINER=$(docker ps -a \
| egrep -v '^ID' \
| awk '{print $1}')
# small trick based on assumption
# that there is just one current
# running docker process,
# wait for resolving this issue:
# https://github.com/dotcloud/docker/issues/252
# Find old image and drop it from docker
OLDIMAGE=$(docker images | egrep "^$IMAGENAME" | awk '{print $3}' | egrep -v '^ID$')
if [[ $OLDIMAGE ]]; then
docker rmi "$OLDIMAGE"
fi
# Make new image
docker commit -m "Added jdk and $APPPATH" "$CONTAINER" "$IMAGENAME"
# Finally remove running container
docker rm "$CONTAINER"
</pre>
<p>
Using:
</p>
<pre class="bash" name="code">$ sudo ./make-image.sh /home/your/app yourcompany/project
</pre>
<p>
Check that image was created:
</p>
<pre class="bash" name="code">$ sudo docker images
</pre>
<h3>
Run image</h3>
<pre class="bash" name="code">
#!/bin/bash
JDK=/usr/lib/jvm/jdk1.7.0
APPPATH=$1
DOCKERIMAGE=$2
SOMEPARAMETER1="1"
SOMEPARAMETER2="2"
# Run docker in daemon mode
(docker -d || echo "Docker daemon already running") &
# Sleep for a few seconds maybe needed here
CONTAINERBASEPATH=/tmp
# Build your command line by line
CMD="cd $CONTAINERBASEPATH/$APPPATH"
#CMD="$CMD ; $CONTAINERBASEPATH/$JDK/bin/java -Xdebug \
# -Xrunjdwp:transport=dt_socket,server=y,address=\"16600\" -jar $APPJAR"
CMD="$CMD ; $CONTAINERBASEPATH/$JDK/bin/java -jar $APPJAR"
# Run app from image under some user rights and with bounded memory
JOB=$(docker run -u irc -m 536870912 -d $DOCKERIMAGE \
/bin/sh -c "$CMD")
#echo "Daemon received: $(docker logs $JOB)"
</pre>
<p>
Using:
</p>
<pre class="bash" name="code">$ sudo ./run-app.sh /home/your/app yourcompany/project "parameter1" "parameter2"
</pre>
<h3>
Reading logs from all running containers</h3>
<pre class="bash" name="code">sudo docker ps -a | grep -v '^ID' | awk '{print $1}' | xargs -n1 sudo docker logs
</pre>
<p>
<i>Note: You can filter containers by image name or something else of course.</i>
</p>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-2259374835286705666.post-64755372960521460892013-04-11T00:59:00.000+07:002013-04-12T13:19:48.812+07:00Improved screencast with Byzanz<p>
<b>Byzanz</b> - small and useful utility for screencast which generate GIF on the output.
</p>
<p>
You can find more information about byzanz <a href="https://wiki.ubuntu.com/CreatingScreencasts">here</a> and <a href="http://manpages.ubuntu.com/manpages/natty/man1/byzanz-record.1.html">here</a>.
</p>
<p>
Typical you are using Byzanz like that:
</p>
<pre class="bash" name="code">
$ byzanz-record --duration=45 --delay=10 --x=0 --y=87 --width=860 --height=520 output.gif
</pre>
<p>
Next, you are trying to hide anything in <b>10</b> seconds and doesn't know exactly when <b>45</b> seconds are, <i>hell</i>, will over.
</p>
<p>
I done small improvement to solve this issue:
</p>
<a name='more'></a>
<pre class="bash" name="code">
$ ( sleep 8 ; notify-send "1..2..3..Go" ; sleep 43 ; notify-send "Few seconds to over" ) & \
byzanz-record --duration=45 --delay=10 --x=0 --y=87 --width=860 --height=520 output.gif ; notify-send "Done"
</pre>
<p>
Now you can hide anything before you will see notification "1..2..3..Go". Next doing your screencast before you will see "Few seconds to over". Finally you will see "Ready".
</p>
<p>
Examples:
</p>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQKyVZ8C1RkvXvYPuRXmTZtLnPCWNenFi-nb3QJJbRdxbLndKUZo3dQhmeshH026Nwl4_vdjxfsTyoeoBm-yeo_ThWfNYAck2Zv1-rLL_IO0EX6OoG6TU5zohzuox5t7ZTXKnM1bxYaWc/s1600/1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="215" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQKyVZ8C1RkvXvYPuRXmTZtLnPCWNenFi-nb3QJJbRdxbLndKUZo3dQhmeshH026Nwl4_vdjxfsTyoeoBm-yeo_ThWfNYAck2Zv1-rLL_IO0EX6OoG6TU5zohzuox5t7ZTXKnM1bxYaWc/s400/1.png" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiM77Achv8AfK70OpDQNgKIeKWR9aKA361ZMDkER6zgy8ZrDJmcL6vsXqOe_Ev_uEXzZ4ESqMwGFM7LaTwQNzCY_ay_BmqpfZZjjm5LRxKm6adv6_3OCbj1-2nyqaKu3q3wNPP2nD9300Y/s1600/2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="222" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiM77Achv8AfK70OpDQNgKIeKWR9aKA361ZMDkER6zgy8ZrDJmcL6vsXqOe_Ev_uEXzZ4ESqMwGFM7LaTwQNzCY_ay_BmqpfZZjjm5LRxKm6adv6_3OCbj1-2nyqaKu3q3wNPP2nD9300Y/s400/2.png" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJomLemmjP8SpKoLvQRdUSYrRs04ueAhI6nBV4hwm01RkbjkCU5a3J0MWt0BTpmxaPdQqb0EMKvjKLNcE1obfpa9i1DhI9ONi7KxuVllIHdbcndxbDoRJKeDGRud9CE3iucXbsJmiiy9Y/s1600/3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;">
<img border="0" height="212" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJomLemmjP8SpKoLvQRdUSYrRs04ueAhI6nBV4hwm01RkbjkCU5a3J0MWt0BTpmxaPdQqb0EMKvjKLNcE1obfpa9i1DhI9ONi7KxuVllIHdbcndxbDoRJKeDGRud9CE3iucXbsJmiiy9Y/s400/3.png" width="400" /></a></div>
<p>
Of Course you should configure byzanz to doesn't record such notifications.
</p>Unknownnoreply@blogger.com2tag:blogger.com,1999:blog-2259374835286705666.post-63101828374253685252011-04-09T00:56:00.007+07:002011-04-13T11:52:18.449+07:00Интерпретация и компиляция Java-кода в Java-программе<div style="text-align: justify;">Давайте представим, что вам понадобилось в своей Java-программе выполнять некоторый код, порождаемый пользователем, в некотором выстроенном контексте. Причем, хотелось бы, чтобы из пользовательского кода были доступны некоторые классы и объекты, определенные в исходном приложении.</div><div style="text-align: justify;">В 6-ой версии платформы Java появилась технология Java Scripting, которая реализована в пакете <a href="http://download.oracle.com/javase/6/docs/api/javax/script/package-summary.html">javax.script</a>. Используя её и какой-либо из реализованных <a href="http://java.net/projects/scripting/">движков</a> (а может быть вы напишите свой движок), можно получить то что мы хотим - Java-программу, исполняющую некоторый код пользователя, который становится известен лишь в runtime. Типично, используют Java Scripting и движок JavaScript (<a href="http://download.oracle.com/javase/6/docs/technotes/guides/scripting/programmer_guide/index.html">ссылка</a>), более того JavaScript движок Rhino от Mozilla входит в JDK 6. Нас же будет интересовать Java-движок.</div><a name='more'></a><br />
<div style="text-align: justify;"><span class="Apple-style-span" style="font-family: arial, sans-serif; font-size: x-large;">Получение и подключение Java-движка к существующему проекту</span></div><div style="text-align: justify;">К сожалению, Java-движок не входит в JDK 6 <span class="Apple-style-span" style="color: #999999;">(интересно, почему ?)</span>, поэтому перед использованием API javax.script придется найти соответствующий jar и подключить его к проекту.</div><br />
<div style="text-align: justify;"><span class="Apple-style-span" style="font-size: large;">Если вы не используете Maven</span></div><span class="Apple-style-span" style="color: #999999; font-size: x-small;">, то скорей начните делать это.</span><br />
<div style="text-align: justify;">Идем <a href="http://java.net/projects/scripting/">сюда</a>, забираем себе директорию <a href="http://java.net/projects/scripting/sources/svn/show">svn</a> / <a href="http://java.net/projects/scripting/sources/svn/show/trunk">trunk</a> / <a href="http://java.net/projects/scripting/sources/svn/show/trunk/engines">engines</a> / <a href="http://java.net/projects/scripting/sources/svn/show/trunk/engines/java">java</a> и при помощи ant собираем java-engine.jar. Осталось лишь подключить java-engine.jar как библиотеку к вашему проекту.</div><br />
<div style="text-align: justify;"><span class="Apple-style-span" style="font-size: large;">Если вы используете Maven</span></div><div style="text-align: justify;">Поискав немного, я нашел <a href="http://www.mvnbrowser.com/artifact-details.html?groupId=com.sun.script&artifactId=java-engine&version=20070316&tab=REFERENCES">то что искал</a> в репозитории <a href="http://repo.fusesource.com/maven2/">http://repo.fusesource.com/maven2/</a>. <span class="Apple-style-span" style="color: #999999;">Попутно я ещё нашел очень полезный сервис <a href="http://www.mvnbrowser.com/">http://www.mvnbrowser.com</a>, позволяющий осуществлять поиск по наиболее популярным открытым Maven репозиториям.</span></div><br />
<div style="text-align: justify;"><span class="Apple-style-span" style="font-family: arial, sans-serif; font-size: x-large;">Простой пример</span></div><pre class="java" name="code">import javax.script.*;
public class Main {
public static void main(String[] args)
throws ScriptException {
ScriptEngineManager manager =
new ScriptEngineManager();
ScriptEngine engine =
manager.getEngineByName("java");
if (engine == null) {
System.err.println(
"Engine error (unknown engine)!");
} else {
engine.put(ScriptEngine.FILENAME,
"TestApp.java");
engine.eval("public class TestApp {"
+ "public static void main(String[] a)"
+ "{System.out.println(\"hello!\");}"
+ "}");
}
}
}
</pre><div style="text-align: justify;">Что здесь происходит ?</div><ol><li>Создаем экземпляр <b>ScriptEngineManager</b>, который позволяет находить конкретный движок (строчки 7-8);</li>
<li>Находим движок java (строчки 9-10);</li>
<li>Так как в Java принято, что имя класса должно совпадать с именем файла, где он располагается, необходимо добавить в контекст движка имя файла <i>TestApp.java</i> (строчки 15-16);</li>
<li>Выполняем скрипт - описание класса <b>TestApp</b> с единственным статическим методом main, что опять же в Java означает, что виртуальная машина вызовет main при исполнении этого кода (строчки 17-20).</li>
</ol><div style="text-align: justify;">Так происходит интерпретация кода, а как быть, если вам необходимо часто исполнять один и тот же кусок кода ? Было бы неплохо, если бы программу можно было скомпилировать в байт-код, что снизило бы затраты на последующую интерпретацию текста.</div><div style="text-align: justify;">В Java Scripting для этих целей предусмотрен интерфейс <b>Compilable</b> с функционалом для компиляции скрипта и его исполнения. Этот интерфейс не обязательно реализуется движком. Нам повезло, используемый Java-движок реализует этот интерфейс, поэтому далее следует версия примера, данного выше, в которой происходит компиляция и выполнение получившегося скомпилированного кода.</div><pre class="java" name="code">import javax.script.*;
public class Main {
public static void main(String[] args)
throws ScriptException {
ScriptEngineManager manager =
new ScriptEngineManager();
ScriptEngine engine =
manager.getEngineByName("java");
if (engine == null) {
System.err.println(
"Engine error (unknown engine)!");
}
else {
engine.put(ScriptEngine.FILENAME,
"TestApp.java");
if (engine instanceof Compilable) {
Compilable comp = (Compilable) engine;
CompiledScript cs = comp.compile(
"public class TestApp {"
+ "public static void main(String[] a)"
+ "{System.out.println(\"hello!\");}"
+ "}");
cs.eval();
} else {
System.err.println(
"Engine error (not compilable)!");
}
}
}
}
</pre><br />
<div style="text-align: justify;"><span class="Apple-style-span" style="font-family: arial, sans-serif; font-size: x-large;">Чуть более сложный пример</span></div><div style="text-align: justify;">Уметь просто исполнять программу в программе конечно полезно, но, видимо, чаще всего необходимо будет оперировать данными внешнего приложения во вложенном или наоборот, получать результат работы скрипта во внешнем приложении. Для этих целей мы можем изменить контекст исполнения вложенной программы, связав в нем какой-либо объект с Java-объект внешней программы. На самом деле мы уже делали это выше, добавляя имя файла в контекст движка</div><div style="text-align: justify;">Далее я покажу, как можно получить доступ к классу внешнего приложения из вложенного и получить доступ к классу вложенного приложения из внешнего.</div><pre class="java" name="code">package testjavaembedded;
import javax.script.*;
public class Main {
public static class Foo {
public String a = "hello";
}
public static interface MyI {
public String m();
}
public static void main(String[] args)
throws ScriptException,
InstantiationException,
IllegalAccessException {
ScriptEngineManager manager =
new ScriptEngineManager();
ScriptEngine engine =
manager.getEngineByName("java");
if (engine == null) {
System.err.println(
"Engine error (unknown engine)!");
} else {
engine.put(ScriptEngine.FILENAME,
"TestApp.java");
engine.getContext().setAttribute("parentLoader",
Thread.currentThread().getContextClassLoader(),
ScriptContext.ENGINE_SCOPE);
Class clazz = (Class) engine.eval(
"import testjavaembedded.Main.Foo;"
+ "import testjavaembedded.Main.MyI;"
+ "public class TestApp implements MyI {"
+ "public String m() {return \"world!\";}"
+ "public static void main(String[] a)"
+ "{"
+ "Foo f = new Foo();"
+ "System.out.println(f.a);}"
+ "}");
Object obj = clazz.newInstance();
MyI my = (MyI) obj;
System.out.println(my.m());
}
}
}
</pre><div style="text-align: justify;">Что здесь происходит ? То же, что и в прошлом примере, за исключением того, что теперь главный класс, приложения, которое исполняется Java-движком, реализует интерфейс <b>testjavaembedded.Main.MyI</b>. Этот интерфейс определен во внешнем приложении. Также, существенно, что теперь в методе <b>main</b> вложенного приложения мы создаем объект класса, определенного во внешнем приложении, и, соответственно, теперь можем вызывать его методы, обращаться к его атрибутам (<b>f.a</b>).</div><div style="text-align: justify;">В прошлом примере это было не важно, но здесь надо заметить, что <b>eval</b> Java-движка вернет тот самый главный класс, который определяется во вложенном приложении (<b>TestApp</b>). Понятно, что мы можем создать объект этого класса и, соответственно, вызывать его методы (строчки 44-46).</div><div style="text-align: justify;">Заработал этот пример не сразу. У меня были такие же проблемы, как и <a href="http://www.sql.ru/forum/actualutils.aspx?action=gotomsg&tid=819172&msg=10045225">здесь</a>. Оказывается для загрузки классов Java-движок использует другой <b>ClassLoader</b>, а не тот же, которым был загружен класс, в методе которого создается этот движок. Решение здесь такое - надо передать в Java-движок правильный <b>ClassLoader</b> и все заработает (строчки 29-31).</div><br />
<div style="text-align: justify;"><span class="Apple-style-span" style="font-family: arial, sans-serif; font-size: x-large;">Безопасность</span></div><div style="text-align: justify;">Отдельного инструмента для безопасного контролируемого выполнения в Java Scripting нет, но есть общий механизм Java Security, который позволяет на уровне виртуальной машины контролировать исполнение кода. Это довольно обширная тема, здесь я попытаюсь лишь показать как можно использовать инструменты Java Security для безопасного выполнения кода Java-движком. Тем кого будут интересовать детали, предлагаю посетить пару ресурсов в ссылках.</div><div style="text-align: justify;">Для того чтобы активировать механизм Java Security, необходимо добавить к параметрам запускаемой виртуальной машины следующие ключи: <b>-Djava.security.manager</b> <b>-Djava.security.policy=<i>security.policy</i></b>. Где <i>security.policy</i> - файл со следующим содержанием:</div><pre>grant {
permission java.security.AllPermission;
};
</pre>это означает, что корневому приложению (внешняя Java-программа) будут предоставлены все права (мы ему доверяем). <span class="Apple-style-span" style="color: #999999;">По умолчанию, при активировании Java Security виртуальная машина исполняет код без каких либо прав.</span><br />
<div style="text-align: justify;">Идея безопасного выполнения скрипта состоит в том, чтобы в контексте корневого приложения, с правами на выполнение любого кода, выстроить новый контекст (<b>getSecureContext</b>), в котором уже нельзя будет исполнять любой код. Далее в этом контексте и исполняется (<b>AccessController.doPrivileged</b>) вложенная потенциально опасная Java-программа.</div><pre class="java" name="code">import java.io.FilePermission;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.CodeSource;
import java.security.Permissions;
import java.security.PrivilegedAction;
import java.security.ProtectionDomain;
import java.util.PropertyPermission;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.script.*;
public class Main {
public static AccessControlContext getSecureContext() {
Permissions perms = new Permissions();
perms.add(new RuntimePermission(
"accessDeclaredMembers"));
perms.add(new RuntimePermission(
"accessClassInPackage.sun.misc"));
perms.add(new RuntimePermission(
"createClassLoader"));
perms.add(new PropertyPermission(
"com.sun.script.java.sourcepath", "read"));
perms.add(new PropertyPermission(
"com.sun.script.java.classpath", "read"));
perms.add(new PropertyPermission(
"java.class.path", "read"));
perms.add(new PropertyPermission(
"java.endorsed.dirs", "read"));
perms.add(new PropertyPermission(
"sun.boot.class.path", "read"));
perms.add(new PropertyPermission(
"java.ext.dirs", "read"));
perms.add(new PropertyPermission(
"nonBatchMode", "read"));
perms.add(new PropertyPermission(
"com.sun.script.java.mainClass", "read"));
perms.add(new FilePermission(
"TestApp.java", "read"));
perms.add(new FilePermission(
"/C:/Program Files/Java/jdk1.6.0_24/-",
"read"));
perms.add(new FilePermission(
"/C:/windows/Sun/Java/-", "read"));
perms.add(new FilePermission(
"/C:/Users/entend/scripting/trunk/engines/java/build/-",
"read"));
perms.add(new FilePermission(
"/C:/Users/entend/NetBeansProjects/TestJavaEmbedded/-",
"read"));
perms.add(new FilePermission(
"TestApp", "read"));
ProtectionDomain domain = new ProtectionDomain(
new CodeSource(null,
(java.security.cert.Certificate[]) null),
perms);
return new AccessControlContext(
new ProtectionDomain[]{domain});
}
public static void main(String[] args)
throws ScriptException {
ScriptEngineManager manager =
new ScriptEngineManager();
final ScriptEngine engine =
manager.getEngineByName("java");
if (engine == null) {
System.err.println(
"Engine error (unknown engine)!");
} else {
ScriptContext newContext =
new SimpleScriptContext();
final Bindings engineScope =
newContext.getBindings(
ScriptContext.ENGINE_SCOPE);
engineScope.put(ScriptEngine.FILENAME,
"TestApp.java");
AccessController.doPrivileged(
new PrivilegedAction() {
public Object run() {
try {
engine.eval(
"public class TestApp {"
+ "public static void main(String[] a)"
+ "{System.exit(0);}"
+ "}", engineScope);
} catch (ScriptException ex) {
System.err.println(ex.getMessage());
}
return null;
}
}, getSecureContext());
}
}
}
</pre><div style="text-align: justify;">Почему такой уродливый контекст, с непереносимо определенными правами (строчки 17-51) ? Я просто пытался запустить код, который ничего не делает, все эти права, как ни странно нужны именно для этого, а потом поленился выяснить, как можно было проще задать эти права. Здесь, кстати, есть тонкий момент, возможно используемый Java-движок не является сам по себе безопасным, и именно поэтому необходима такая куча прав для исполнения самого безобидного кода. А вложенная программа будет запущена как раз с теми же правами, что и <b>eval</b>, поэтому надо тщательно выбирать движок. </div><div style="text-align: justify;">Если вы попытаетесь теперь выполнить полученную программу с ключом <b>-Djava.security.debug=access</b>, то получите следующую ошибку:</div><pre>...
access: access denied (java.lang.RuntimePermission exitVM.0)
java.lang.reflect.InvocationTargetException
</pre><div style="text-align: justify;">Нельзя завершать работу виртуальной машины, без имеющихся на то прав.</div><div style="text-align: justify;"><span class="Apple-style-span" style="font-family: arial, sans-serif; font-size: x-large;">Ссылки</span></div><ul><li><a href="http://www.ibm.com/developerworks/ru/library/j-javascripting1/">http://www.ibm.com/developerworks/ru/library/j-javascripting1</a>;</li>
<li><a href="http://download.oracle.com/javase/6/docs/technotes/guides/scripting/programmer_guide/index.html">http://download.oracle.com/javase/6/docs/technotes/guides/scripting/programmer_guide/index.html</a>;</li>
<li><a href="http://voituk.kiev.ua/2008/08/18/bezopastnost-v-java/">http://voituk.kiev.ua/2008/08/18/bezopastnost-v-java</a>;</li>
<li><a href="http://worldwizards.blogspot.com/2009/08/java-scripting-api-sandbox.html">http://worldwizards.blogspot.com/2009/08/java-scripting-api-sandbox.html</a>;</li>
</ul>Unknownnoreply@blogger.com1tag:blogger.com,1999:blog-2259374835286705666.post-64197727249961291892011-03-07T13:09:00.011+06:002011-04-07T19:06:38.135+07:00Повторяемые задания<div style="text-align: justify;">Встретился с интересной задачкой.</div><div style="text-align: justify;">Есть список некоторых заданий. Что это за задания нам не интересно, главное что они как-то распределены во времени.</div><div style="text-align: justify;">Существуют два типа заданий:</div><ul><li><b>разовые</b>;</li>
<li><b>повторяемые</b>.</li>
</ul><div style="text-align: justify;">Каждое из них, думаю, допустимо описать парой: <i>(дата окончания, паттерн повторяемости)</i>, где:</div><ul><li><i>дата окончания</i> - дата, до которой данное задание должно быть выполнено, включительно;</li>
<li><i>паттерн повторяемости</i> - шаблон по которому создаются новые задания. Задание <b>разовое</b> - пуст, <b>повторяемое</b> - имеет свою структуру:</li>
<ul><li><i>тип повторяемости</i> - один из вариантов: 'ежедневно', 'еженедельно', 'ежемесячно', 'ежегодно';</li>
<li><i>повторять через</i> - число дней/недель/месяцев/годов;</li>
<li><i>длительность задания</i> - d-дней, m-месяцев, y-лет.</li>
</ul></ul><div style="text-align: justify;">Думаю с <b>разовыми</b> заданиями все очевидно, задание необходимо выполнить до указанной <i>даты окончания</i>, больше оно нас не будет тревожить. Что же касается <b>повторяемых</b> заданий, то тут есть проблемы.</div><div style="text-align: justify;">На самом деле, <b>повторяемое</b> задание - это одно <b>разовое</b> задание, которое нужно выполнить до указанной <i>даты окончания</i> и шаблон по которому будут созданы новые <b>разовые</b> задания. Вопросы, связанные с повторяемыми заданиями, которые нас будут интересовать:</div><ol><li>Как определить, необходимо ли сегодня (DD.MM.YY) создавать новое <b>разовое</b> задание для некоторого <b>повторяемого</b> ? Будем считать, что задание может быть создано за 1 день до официального начала.</li>
<li>Как назначить <i>дату окончания</i> нового разового задания ?</li>
</ol><a name='more'></a><br />
<div style="text-align: justify;">Прежде чем переходить к ответу на эти вопросы для общей задачи, давайте придумаем примеры <b>повторяемых</b> заданий, т.е. таких пар <i>(дата окончания, паттерн повторяемости)</i>, и попытаемся ответить на эти вопросы для них.</div><br />
<div style="text-align: justify;"><span class="Apple-style-span" style="font-family: inherit; font-size: x-large;">Примеры заданий</span></div><br />
<div style="text-align: justify;"><span class="Apple-style-span" style="font-family: inherit; font-size: large;"><b>Задание 1</b></span></div><div style="text-align: justify;"><i>дата окончания</i>: 15.02.11</div><div style="text-align: justify;"><i>повторять</i>: каждый 1 месяц</div><div style="text-align: justify;"><i>длительность</i>: 1 месяц</div><div style="text-align: justify;"><br />
</div><div style="text-align: justify;"><b>Предварительные замечания</b>:</div><div style="text-align: justify;">Если <i>дата окончания</i> - 15.02.11 и <i>длительность</i> 1 месяц => начало задание - 15.01.11.</div><br />
<div style="text-align: justify;"><u>Пусть сегодня: 13.02.11</u>.</div><ol><li>Так как 15.01.11 + 1 месяц > 13.02.11 + 1 день => ещё рано;</li>
<li>-</li>
</ol><br />
<div style="text-align: justify;"><u>Пусть сегодня: 16.02.11</u>.</div><ol><li>Так как 15.01.11 + 1 месяц <= 16.02.11 + 1 день => можно начинать;</li>
<li>Так как <i>повторяемость</i> - 1 месяц =><i> дата окончания</i> нового задания - 15.02.11 + 1 месяц = 15.03.11.</li>
</ol><br />
<div style="text-align: justify;"><u>Пусть сегодня: 14.03.11</u>.</div><ol><li>Так как 15.03.11 + 1 месяц <= 14.03.11 + 1 день => можно начинать;</li>
<li>Так как <i>повторяемость</i> - 1 месяц => <i>дата окончания</i> нового задания - 15.03.11 + 1 месяц = 15.03.11.</li>
</ol><br />
<div style="text-align: justify;"><span class="Apple-style-span" style="font-family: inherit; font-size: large;"><b>Задание 2</b></span></div><div style="text-align: justify;"><i>дата окончания</i>: 15.02.11</div><div style="text-align: justify;"><i>повторять</i>: каждый 1 год</div><div style="text-align: justify;"><i>длительность</i>: 2 дня и 3 месяца</div><br />
<div style="text-align: justify;"><b>Предварительные замечания</b>:</div><div style="text-align: justify;">Если <i>дата окончания</i> - 15.02.11 и <i>длительность</i> 2 дня и 3 месяц => начало задания - 13.11.10.</div><br />
<div style="text-align: justify;"><u>Пусть сегодня: 16.02.11</u>.</div><ol><li>Так как 13.11.10 + 1 год > 16.02.11 + 1 день => ещё рано;</li>
<li>-</li>
</ol><br />
<div style="text-align: justify;"><u>Пусть сегодня: 12.11.11</u>.</div><ol><li>Так как 13.11.10 + 1 год <= 12.11.11 + 1 день => можно начинать;</li>
<li>Так как <i>повторяемость</i> - 1 год =><i> дата окончания</i> нового задания - 15.02.11 + 1 год = 15.02.12.</li>
</ol><br />
<div style="text-align: justify;"><u>Пусть сегодня: 12.11.12</u>.</div><ol><li>Так как 13.11.11 + 1 год <= 12.11.11 + 1 день => можно начинать;</li>
<li>Так как <i>повторяемость</i> - 1 год => <i>дата окончания</i> нового задания - 15.02.12 + 1 год = 15.02.13.</li>
</ol><br />
<div style="text-align: justify;"><span class="Apple-style-span" style="font-family: inherit; font-size: x-large;">Алгоритмы решения</span></div><div style="text-align: justify;"><u>Пусть</u>:</div><div style="text-align: justify;"><i>Сегодня</i>: D1.M1.Y1</div><div style="text-align: justify;"><i>дата окончания</i>: D0.M0.Y0 (первого задания)</div><div style="text-align: justify;"><i>дата окончания</i>: D2.M2.Y2 (предыдущего задания)</div><div style="text-align: justify;"><i>повторять</i>: каждый A день/неделю/месяц/год</div><div style="text-align: justify;"><i>длительность</i>: d-дней, m-месяцев, y-лет.</div><br />
<div style="text-align: justify;"><span class="Apple-style-span" style="font-size: x-large;">Алгоритм 1</span></div><div style="text-align: justify;">То что первое пришло в голову. Давайте будем действовать также, как мы решали задачки в примерах. Т.е. принимать решение о создании нового задания на основе данных о прошлой <b>разовой</b> задаче, созданной по шаблону данной <b>повторяемой</b>.</div><div style="text-align: justify;">Тогда:</div><ol><li>Если D1.M1.Y1 + 1 день >= D2.M2.Y2 - (d + m + y) + A => можно начинать, иначе ещё рано;</li>
<li><i>Дата окончания</i> нового задания - D2.M2.Y2 + A.</li>
</ol><div style="text-align: justify;">Ок, это будет работать, но только в том случае, если мы можем сохранять последнюю <b>разовую </b>задачу, т.е. каждый раз мы должны знать D2.M2.Y2, что не всегда возможно.</div><br />
<div style="text-align: justify;"><span class="Apple-style-span" style="font-size: x-large;">Алгоритм 2</span></div><div style="text-align: justify;">Здесь я предполагаю описать алгоритм, который бы не зависел от предыдущей задачи, и которому было бы достаточно только <i>даты окончания</i> первого задания и <i>паттерна повторяемости</i>.</div>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-2259374835286705666.post-35568156184650973472010-08-20T22:12:00.004+07:002011-04-07T02:35:47.425+07:00Sikuli играет в игру LPairs<div style="text-align: justify;"><a href="http://groups.csail.mit.edu/uid/sikuli">Sikuli</a> - инструмент для автоматизации работы с элементами GUI, играет в <a href="http://lgames.sourceforge.net/index.php?project=LPairs">LPairs</a> (простую игру для укрепления памяти).</div><br />
<div style="text-align: justify;"><span class="Apple-style-span" style="font-size: x-large;">Скрипт</span></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhho3Fh9rsloRSCSRbn8gXZTyIoWmtUrsHT1R8y_lszeyLBZzdk7xs3UivlouMM_HUmyhQTUIjOzfbNNEcAK-BlLoKHi1GftIQOI4aSi4XzEGgLJx3BrKeaFx2RzOgeDX1KGABDyk_9-74/s1600/sikuli-lpairs.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhho3Fh9rsloRSCSRbn8gXZTyIoWmtUrsHT1R8y_lszeyLBZzdk7xs3UivlouMM_HUmyhQTUIjOzfbNNEcAK-BlLoKHi1GftIQOI4aSi4XzEGgLJx3BrKeaFx2RzOgeDX1KGABDyk_9-74/s640/sikuli-lpairs.png" width="411" /></a></div><a name='more'></a><br />
<div style="text-align: justify;"><span class="Apple-style-span" style="font-size: x-large;">Видео</span></div><object height="385" width="480" style="clear: both; text-align: center;"><param name="movie" value="http://www.youtube.com/v/2w44dPPeAAc?fs=1&hl=ru_RU"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/2w44dPPeAAc?fs=1&hl=ru_RU" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="480" height="385"></embed></object>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-2259374835286705666.post-40276922172209154962010-08-05T00:25:00.014+07:002011-04-07T18:58:54.594+07:00Параллельное выполнение команд: GNU Parallel<div style="text-align: justify;">Проект GNU Parallel развивается с 2007 года и представляет собой утилиту для выполнения задач в shell параллельно в текущей и/или удаленных системах. Задачи формируются из скриптов и команд операционной системы, и параметров (или частей команд) из стандартного ввода. В этом GNU Parallel похож на xargs, в задачи которого входит формирование команд из данных со стандартного ввода, по заявлению разработчика, GNU Parallel даже имеет схожие с xargs опции.</div><div style="text-align: justify;">Схожесть с xargs позволяет использовать GNU Parallel в ранее созданных скриптах, в сложных конвейерах, используя все предоставленные ресурсы имеющихся систем (ядра/процессоры текущей или удаленных многоядерных/многопроцессорных систем).</div><a name='more'></a><br />
<div style="text-align: justify;">Задачи, в решении которых целесообразно использовать GNU Parallel:</div><ul><li>Многократное выполнение операций над группой файлов (перекодирование аудио/видео, обработка текста). Важным критерием является продолжительность одной операции. Чем больше времени требуется на выполнение одной операции, тем лучший результат даст использование GNU Parallel;</li>
<li>Расчет мат. задач по подготовленному плану (в виде последовательности shell-команд).</li>
</ul><br />
<div style="text-align: justify;"><span class="Apple-style-span" style="font-size: x-large;">Установка</span></div><div style="text-align: justify;">Разработчик GNU Parallel предлагает выполнить установку командой ./configure && make && make install в директории с исходными кодами утилиты. У меня есть другое решение.</div><div style="text-align: justify;">GNU Parallel в текущей редакции представляет собой один скрипт parallel на perl. Я предлагаю не выполнять make install, а проделать следующее, в случае если вы используете bash в качестве shell'а:</div><ul><li>Создать директорию <i>~/.bin</i> - здесь будут размещаться исполняемые файлы доступные пользователю ;</li>
<li>Скопировать файл <i>parallel</i> в <i>~/.bin</i> ;</li>
<li>Добавить в конец файла <i>~/.bash_profile</i> строку PATH=$PATH:/home/USERNAME/.bin - т.е. добавить к переменной PATH путь к исполняемым файлам, доступным пользователю. Возможно потребуется также править <i>~/.bashrc</i> .</li>
</ul><div style="text-align: justify;">Таким образом, можно установить GNU Parallel на любую машину с unix-подобной ОС, установленным bash и perl, при наличии лишь прав на запись и выполнение в домашней директории.</div><br />
<div style="text-align: justify;"><span class="Apple-style-span" style="font-size: x-large;">Пример 1</span></div><div style="text-align: justify;"><b>Задача:</b> Перекодировать все mp3 файлы в текущей директории в wav.</div><div style="text-align: justify;"><b>Ресурсы:</b><br />
Intel(R) Core(TM)2 Duo CPU E6550 @ 2.33GHz (2 ядра).</div><div style="text-align: justify;"><b>Решение:</b></div><pre>$ find *.mp3 -print0 | /usr/bin/time xargs -0 -n1 -I{} lame {} -o {}.wav
591.59user 1.73system 9:54.96elapsed 99%CPU (0avgtext+0avgdata 9424maxresident)k 0inputs+0outputs (0major+28668minor)pagefaults 0swaps</pre><div style="text-align: justify;"><b>Решение с использованием GNU Parallel:</b></div><pre>$ /usr/bin/time parallel -j+0 lame {} -o {.}.wav ::: *.mp3
593.11user 1.94system 5:05.27elapsed 194%CPU (0avgtext+0avgdata 24208maxresident)k 0inputs+0outputs (0major+68797minor)pagefaults 0swaps</pre><div style="text-align: justify;"><b>Вывод:</b></div><div style="text-align: justify;">GNU Parallel в решении данной задачи более эффективно использовал предоставленные ресурсы (194%CPU против 99%CPU), что позволило сократить время решения задачи с ~10 минут до ~5 минут. P.S. все файлы были размещены на ramfs.</div><br />
<div style="text-align: justify;"><span class="Apple-style-span" style="font-size: x-large;">Пример 2</span></div><div style="text-align: justify;"><b>Задача:</b> Факторизовать числа от 17999999900000 до 17999999999999.</div><div style="text-align: justify;"><b>Ресурсы:</b><br />
Intel(R) Core(TM)2 Duo CPU E6550 @ 2.33GHz (2 ядра).<br />
Celeron (Coppermine) 791MHz (1 процес., удал. система).<br />
2 ядра удал. системы.</div><div style="text-align: justify;"><b>Замечание:</b></div><div style="text-align: justify;">В данном случае нас интересует время решения задачи, а не собственно разложение на множители, поэтому создадим <i>~/.bin/myfactor</i> - утилита на базе утилиты factor, факторизующая число, но не выводяющая результат в стандартный вывод. Утилита myfactor должна быть распространена на все удаленные системы, которые будут участвовать в решении задачи.</div><div style="text-align: justify;"><i>~/.bin/myfactor:</i></div><pre class="bash" name="code">#!/bin/sh
factor $* > /dev/null</pre><pre>$ ls -l ~/.bin/myfactor
-rwxr-xr-x 1 USERNAME USERNAME 33 Авг 4 20:42 /home/USERNAME/.bin/myfactor</pre><div style="text-align: justify;"><b>Решение:</b></div><pre>$ seq 17999999900000 17999999999999 | /usr/bin/time xargs -n1 myfactor
224.74user 69.58system 6:24.55elapsed 76%CPU (0avgtext+0avgdata 4736maxresident)k 0inputs+0outputs (0major+67839100minor)pagefaults 0swaps</pre><div style="text-align: justify;"><b>Решение с использованием GNU Parallel:</b></div><div style="text-align: justify;">Чтобы наиболее эффективно решить данную задачу, задействовав все предоставленные ресурсы, необходимо использовать возможность GNU Parallel соединяться с удаленными системами через ssh-протокол. Существуют несколько способов сообщить GNU Parallel о конфигурации удаленных систем, я использовал параметр --sshloginfile, который ссылался на файл <i>~/parallelservers</i>, где были описаны все удаленные системы.</div><div style="text-align: justify;">Тут стоит заметить, что использовать возможность GNU Parallel соединяться с удаленными системами целесообразно, только если включен механизм OpenSSH, называемый <a href="http://www.linux.com/archive/feature/54498">ControlMaster</a>. Иначе, GNU Parallel связывает с каждой командой новую ssh-сессию, что негативно сказывается на общем времени выполнения.</div><div style="text-align: justify;">Также, стоит обратить внимание на опцию -j+0, в случае её отсутствия, независимо от содержимого <i>~/parallelservers</i>, на удаленных системах будет запущено по 9 (default) задач и по 9 (и больше) ssh-соединений. В большинстве случаев это приводит к отключению от sshd с ошибкой, соответствующей превышению лимита соединений.</div><pre>$ seq 17999999900000 17999999999999 | /usr/bin/time parallel -j+0 --sshloginfile ~/parallelservers myfactor
249.12user 183.46system 5:00.61elapsed 143%CPU (0avgtext+0avgdata 24000maxresident)k 0inputs+0outputs (0major+106855307minor)pagefaults 0swaps</pre><div style="text-align: justify;"><b>Вывод:</b></div><div style="text-align: justify;">GNU Parallel использовал 2 ядра текущей системы (143%CPU против 76%CPU), а также все доступные ресурсы удаленных систем, что в итоге позволило решить задачу на ~1.5 минуты быстрее.</div><br />
<div style="text-align: justify;"><span class="Apple-style-span" style="font-size: x-large;">Общая схема использования</span></div><div style="text-align: justify;">Если перейти по приведенным выше ссылкам, то можно ознакомится с примерами разработчика по использованию GNU Parallel. Я считаю, что помимо примеров по использованию этой утилитой полезно будет рассмотреть общую схему использования.</div><div style="text-align: justify;">Я вижу общую схему использования GNU Parallel так:</div><ul><li>Установить GNU Parallel предложенным выше способом на все машины, где у вас есть ssh-доступ;</li>
<li>Составить список всех доступных машин в файле, путь к которому будет указываться параметром --sshloginfile;</li>
<li>Включить механизм <a href="http://www.linux.com/archive/feature/54498">ControlMaster</a>;<br />
</li>
<li>Разбивать сложную (и требующую длительного решения) задачу на несколько длительных подзадач;</li>
<li>Выполнить задачу, разбитую на подзадачи при помощи GNU Parallel.</li>
</ul><div style="text-align: justify;">Плюсы от использования данной схемы:</div><ul><li>Использование GNU Parallel схоже с использованием xargs, что упрощает построение параллельных команд;</li>
<li>Использование всех доступных ресурсов текущей системы (все ядра и процессоры);</li>
<li>Чтобы получить в свое распоряжение ресурсы удаленных систем (bash+perl), достаточно иметь ssh-доступ и право на запись и выполнение в домашней директории.</li>
</ul><div style="text-align: justify;">Последнее выгодно отличает GNU Parallel и эту схему, в частности, от использования сложных инструментов, и позволяет распараллеливать задачу обычному пользователю без установки дополнительных демонов, сторонних ядер, написании сложного кода. Однако GNU Parallel и эта схема, в частности, применима для решения существенно меньшего количества задач.</div><br />
<div style="text-align: justify;"><span class="Apple-style-span" style="font-size: x-large;">Ссылки</span></div><ul><li><a href="http://www.gnu.org/software/parallel">Домашняя страница GNU Parallel</a>;</li>
<li><a href="http://savannah.gnu.org/projects/parallel">Документация GNU Parallel</a>;</li>
<li><a href="http://www.youtube.com/watch?v=OpaiGYxkSuQ">Использование GNU Parallel, видео, часть 1</a>;</li>
<li><a href="http://www.youtube.com/watch?v=P40akGWJ_gY">Использование GNU Parallel, видео, часть 2</a>.</li>
</ul>Unknownnoreply@blogger.com2tag:blogger.com,1999:blog-2259374835286705666.post-3876062294130336492010-07-07T17:27:00.019+07:002011-04-07T19:04:15.919+07:00Утилиты для аудита исходных кодов: Graudit, RATS<div style="text-align: justify;"><span class="Apple-style-span" style="font-family: Arial;"><span class="Apple-style-span">Бесспорно полезно проводить аудит исходных кодов с целью выявления потенциально проблемных и опасных участков кода. В случае, если эта операция автоматизирована при помощи какой-то утилиты, то также это не отнимет много времени.</span></span></div><div style="text-align: justify;"><span class="Apple-style-span" style="font-family: Arial;"><span class="Apple-style-span">Пробовал я утилиты для автоматизированного аудита исходных кодов на задачах 1-го курса университета (ЯП c), расположенных в ~/1-course.</span></span></div><a name='more'></a><br />
<div style="text-align: justify;"><span class="Apple-style-span" style="font-family: Arial; font-size: x-large;"><b>Graudit</b></span></div><div style="text-align: justify;"><span class="Apple-style-span" style="font-family: Arial;"><a href="http://github.com/wireghoul/graudit/">http://github.com/wireghoul/graudit/</a></span></div><span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;">Graudit представляет из себя простой скрипт (использующий grep) и набор сигнатур для поиска потенциальных опасных участков в исходных кодах.</span><br />
<div style="text-align: justify;"><span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;">Поддерживаются следующие ЯП: asp, jsp, c, perl, php, python, dotnet.</span></div><div style="text-align: justify;"><span class="Apple-style-span" style="font-family: Arial;"><i><b><span class="Apple-style-span">Пробуем</span></b></i></span></div><pre>$ graudit ~/1-course</pre><div style="text-align: justify;"><span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;">Graudit (версии 1.6) считает, что мои задачи 1-го курса <s>не содержат</s> потенциально опасных участков кода.</span></div><br />
<div style="text-align: justify;"><span class="Apple-style-span" style="font-family: Arial; font-size: x-large;"><b>RATS</b></span></div><div style="text-align: justify;"><span class="Apple-style-span" style="font-family: Arial;"><a href="http://www.fortify.com/security-resources/rats.jsp">http://www.fortify.com/security-resources/rats.jsp</a></span></div><div style="text-align: justify;"><span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"><span class="Apple-style-span" style="color: #282828; line-height: 16px;">Rough Auditing Tool for Security</span>.</span></div><div style="text-align: justify;"><span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;">RATS - утилита для обнаружения популярных ошибок программистов, влияющих на безопасность кода. </span></div><div style="text-align: justify;"><span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;">Если верить описанию, то RATS более продвинутый инструмент, нежели Graudit. Способен, скажем, находить потенциальное переполнение буфера, помимо отслеживания вызова небезопасных функций.</span></div><div style="text-align: justify;"><span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;">Поддерживаются следующие ЯП: c, cpp, perl, php, python.</span></div><div style="text-align: justify;"><span class="Apple-style-span" style="font-family: Arial;"><i><b><span class="Apple-style-span">Пробуем</span></b></i></span></div><pre>$ rats ~/1-course
1-course/12_9.cpp:77: High: fixed size local buffer
1-course/12_9.cpp:78: High: fixed size local buffer
1-course/13_4.cpp:68: High: fixed size local buffer
1-course/13_4.cpp:129: High: fixed size local buffer
Extra care should be taken to ensure that character arrays that are allocated
on the stack are used safely. They are prime targets for buffer overflow
attacks.
1-course/12_9.cpp:97: High: strcat
Check to be sure that argument 2 passed to this function call will not copy
more data than can be handled, resulting in a buffer overflow.
1-course/12_9.cpp:100: High: strcpy
Check to be sure that argument 2 passed to this function call will not copy
more data than can be handled, resulting in a buffer overflow.
1-course/16_28.cpp:131: High: scanf
1-course/16_28.cpp:136: High: scanf
1-course/16_28.cpp:159: High: scanf
1-course/15_3.cpp:47: High: scanf
1-course/15_3.cpp:61: High: scanf
Check to be sure that the format string passed as argument 2 to this function
call does not come from an untrusted source that could have added formatting
characters that the code is not prepared to handle. Additionally, the format
string could contain `%s' without precision that could result in a buffer
overflow.
1-course/1_34.cpp:61: Medium: read
1-course/12_9.cpp:69: Medium: read
1-course/12_9.cpp:142: Medium: read
1-course/12_9.cpp:172: Medium: read
Check buffer boundaries if calling this function in a loop and make sure you are not in danger of writing past the allocated space.
Total lines analyzed: 2638
Total time 0.004944 seconds
533576 lines per second</pre><div style="text-align: justify;"><span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;">Оказывается мои задачи с 1-го курса все же не идеальны.</span></div><div style="text-align: justify;"><span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;">Что именно не понравилось RATS в моем коде ?</span></div><div style="text-align: justify;"><span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"><i><b>12_9.cpp</b></i></span></div><pre class="cpp" name="code">// ...
/* 69 */ in.read(&b,1);
// ...
int somefunc(const char* str1,const char* str2,int sz){
// ...
/* 77 */ unsigned char c1 = (unsigned char)str1[i];
/* 78 */ unsigned char c2 = (unsigned char)str2[i];
// ...
}
// ...
/* 97 */ strcat(strcat(res,token)," ");
// ...
/* 100 */ strcpy(buffer,res);
// ...
</pre><div style="text-align: justify;"><span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;">Как видно, str1 и str2 - это не fixed size local buffer, это указатели, хотя безусловно код опасен. Что касается strcat, strcpy и read, то предупреждения RATS адекватны.</span></div><div style="text-align: justify;"><span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"><i><b>13_4.cpp</b></i></span></div><pre class="cpp" name="code">// ...
/* 68 */ char num[25];
// ...
/* 129 */ char num[25];
// ...
</pre><div style="text-align: justify;"><span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;">Здесь согласен, опасность исходит именно от использования fixed size local buffer.</span></div><div style="text-align: justify;"><span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"><b>16_28.cpp</b>, <b>15_3.cpp</b> и <b>1_34.cpp</b> не особо интересны.</span></div><br />
<div style="text-align: justify;"><span class="Apple-style-span" style="font-size: x-large;">Итог</span></div><div style="text-align: justify;"><span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;">Graudit обнаруживает вхождение сигнатур из своей базы в анализируемых исходных кодах, но за счет скудной базы он пока (версия 1.6) не пригоден даже для простых (мои задачи 1-го курса) задач. Хотя на некоторых python-скриптах Graudit обнаруживал проблемы, но за счет не менее скудного вывода сообщений о проблемах, понять что он хотел сообщить я не смог.</span></div><div style="text-align: justify;"><span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;">RATS - интересная утилита, удобно группирует вывод сообщений о проблемах в исходном коде, сообщения адекватны. Ключом -w RATS можно настроить на вывод различного класса сообщений о проблемах (по умолчанию, о проблемах с маркером Low не сообщается).</span></div>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-2259374835286705666.post-32604827618091917352010-04-24T01:26:00.007+07:002011-04-07T03:14:25.465+07:00Стул в Blender из скрипта на python<div style="text-align: justify;"><span class="Apple-style-span" style="font-size: x-large;">Задача</span></div><div style="text-align: justify;">Нарисовать стул в Blender из скрипта на python.</div><div style="text-align: justify;"><span class="Apple-style-span" style="font-size: x-large;">Решение</span></div><pre class="python" name="code">from Blender import *
# Clean scene
def newactivescene():
sc = Scene.New()
oldsc = Scene.GetCurrent()
sc.makeCurrent()
Scene.Unlink(oldsc)
return sc
sc = newactivescene()
# Camera
camera = Camera.New()
camob = Object.New('Camera')
camob.link(camera)
sc.link(camob)
camob.setLocation(3.5,-5,5.8)
camob.setEuler(60*2*3.14/360,0,30*2*3.14/360)
sc.setCurrentCamera(camob)
# Lamp
lamp = Lamp.New('Sun')
lampob = Object.New('Lamp')
lampob.link(lamp)
sc.link(lampob)
lampob.setLocation(2,-2.5,7)
lampob.setEuler(60*2*3.14/360,0,30*2*3.14/360)
# Legs height, weight
height = 1.9
weight = 1.8
# Legs
sc.objects.new(Mesh.Primitives.Cylinder(100,0.5,height)).\
setLocation(-weight/2,weight/2,height/2)
sc.objects.new(Mesh.Primitives.Cylinder(100,0.5,height)).\
setLocation(weight/2,weight/2,height/2)
sc.objects.new(Mesh.Primitives.Cylinder(100,0.5,height)).\
setLocation(weight/2,-weight/2,height/2)
sc.objects.new(Mesh.Primitives.Cylinder(100,0.5,height)).\
setLocation(-weight/2,-weight/2,height/2)
# Seat
sc.objects.new(Mesh.Primitives.Plane(weight+0.5)).\
setLocation(0,0,height)
# The back of
ob = sc.objects.new(Mesh.Primitives.Plane(weight+0.5))
ob.setLocation(0,(weight+0.5)/2,height+(weight+0.5)/2)
ob.setEuler(90*2*3.14/360,0,0)
Window.RedrawAll()
</pre><a name='more'></a><br />
<span class="Apple-style-span" style="font-size: x-large;">Пример</span><br />
<div style="text-align: justify;">Выполнить python-скрипт в Blender можно несколькими способами, один из них (linux):</div><pre name="code">$ blender -P scriptname.py
</pre><br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgPuwH9uWfuj-mHk5m-j_q8ogQtEs3TwJLfqkquq6vl6SORMHaEYQFcGqxyX5NZfv8Hl_ZFFdAgHmjddzptJjJR-5_05J49uufYuARMT0Zfiw73Ge6CgvHxb-VMcquqgyqaqNSkmntUyDE/s1600/chair.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgPuwH9uWfuj-mHk5m-j_q8ogQtEs3TwJLfqkquq6vl6SORMHaEYQFcGqxyX5NZfv8Hl_ZFFdAgHmjddzptJjJR-5_05J49uufYuARMT0Zfiw73Ge6CgvHxb-VMcquqgyqaqNSkmntUyDE/s320/chair.png" /></a></div><br />
<div style="text-align: justify;"><span class="Apple-style-span" style="font-size: x-large;">Ссылки</span></div><ul><li><a href="http://blender3d.org.ua/tutorial/371.html">Blender: первые шаги в Python</a>.</li>
</ul>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-2259374835286705666.post-74357239547635707432010-04-11T18:26:00.009+07:002011-04-07T03:27:36.556+07:00Удаление содержимого html по маркеру в тегах на языке python<div style="text-align: justify;"><span class="Apple-style-span" style="font-size: x-large;">Задача</span></div><div style="text-align: justify;">Оставить в строке, содержащей html код, всё кроме содержимого тегов (и самих тегов) помеченных определенным маркером ("ignoremarker").</div><a name='more'></a><br />
<div style="text-align: justify;"><span class="Apple-style-span" style="font-size: x-large;">Решение</span></div><div style="text-align: justify;">Воспользуемся функционалом python класса HTMLParser и реализуем функцию, принимающую на вход строку с html кодом и маркер, а на выходе формирующую требуемый html код. Работаем в предположении, что html код на входе - валидный html.</div><pre class="python" name="code">from HTMLParser import HTMLParser
def strip(str, ignoremarker):
class StrippingParser(HTMLParser):
def __init__(self, ignoremarker):
HTMLParser.__init__(self)
self.stack = []
self.openignores = []
self.ignoremarker = ignoremarker
def handle_data(self, data):
if not len(self.openignores):
self.stack.append(data)
def handle_starttag(self, tag, attrs):
if len(self.openignores):
if self.ignoremarker in dict(attrs):
self.openignores.append(tag)
elif not (self.ignoremarker in dict(attrs)):
self.stack.append('<%s%s>' % (tag, self.__html_attrs(attrs)))
else:
self.openignores.append(tag)
def handle_startend_tag(self, tag, attrs):
if not len(self.openignores) and \
not (self.ignoremarker in dict(attrs)):
self.stack.append('<%s%s/>' % (tag, self.__html_attrs(attrs)))
def handle_endtag(self, tag):
if not len(self.openignores):
self.stack.append('' % (tag))
elif tag in self.openignores:
self.openignores.pop()
def __html_attrs(self, attrs):
_attrs = ''
if attrs:
_attrs = ' %s' % (' '.join([('%s="%s"' % (k,v)) for k,v in attrs.iteritems()]))
return _attrs
stripparser = StrippingParser(ignoremarker)
stripparser.feed(str)
stripparser.close()
return ''.join(stripparser.stack)
</pre><div style="text-align: justify;"><span class="Apple-style-span" style="font-size: x-large;">Пример использования</span></div><pre class="python" name="code">teststr = """
Hello
<div ignoremarker="">,<span ignoremarker="">burn in hell</span> </div>and be happy!
"""
print strip(teststr, "ignoremarker")
</pre><pre>Hello and be happy
</pre><div style="text-align: justify;"><span class="Apple-style-span" style="font-size: x-large;">Ссылки</span></div><ul><li>Часть функционала была взята <a href="http://unethicalblogger.com/node/180">здесь.</a></li>
</ul>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-2259374835286705666.post-13411938631481436042010-02-04T23:41:00.011+06:002011-04-07T03:16:34.896+07:00Stunnel выручил<div style="text-align: justify;"><span class="Apple-style-span" style="font-size: x-large;">Преамбула</span></div><div style="text-align: justify;">Обслуживая один удаленный веб-сервис с доступом only HTTP, решил изменить (не без оснований) настройки cookies. В частности, переключил настройку cookies с cookies over HTTP на only secure cookies (over HTTPS).</div><a name='more'></a><br />
<div style="text-align: justify;"><span style="font-size: x-large;">История</span></div><div style="text-align: justify;">Только выполнив переключение, понял что совершил фатальную ошибку. Система аутентификации, как оказалось, зависила от cookies и без них отказывалась пускать хоть кого-нибудь, даже администратора. Сервис отвечал только по HTTP, а cookies работали только через HTTPS. Без физического доступа поправить сделанное было возможно только через панель администратора сервиса, к которой, как и ко всему другому, нельзя было добраться. Физического доступа к сервису не было и получить его в ближайшее время не представлялось возможным.</div><div style="text-align: justify;">Перепробовал несколько веб-браузеров, различные настройки, в надежде, что как-то можно будет обойти зависимость системы аутентификации от cookies, но тщетно.</div><div style="text-align: justify;">Через некоторое время решил попробовать stunnel с такой вот нехитрой конфигурацией:</div><pre>[helpme]
accept = 20000
connect = web-service-ip:web-service-port</pre><div style="text-align: justify;">Таким образом, создав SSL-тунель от localhost:20000 к web-service-ip:web-service-port, я фактически получил веб-сервис с HTTPS доступом. Подключившись к https://localhost:20000, смог поправить необходимую опцию веб-сервиса.</div>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-2259374835286705666.post-80587397088440429562010-01-31T14:54:00.006+06:002011-04-07T03:28:48.158+07:00Subversion 1.6.9 - svn:externals<pre>$ svn propset svn:externals 'object svn://svnserver/externalrepos/object' .
</pre><div style="text-align: justify;">Теперь object внешняя по отношению к данному репозиторию с содержимым из svn://svnserver/externalrepos/object.</div><div style="text-align: justify;">Надеюсь в следующих версиях синтаксис изменится.</div>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-2259374835286705666.post-8960271654942894402009-08-21T12:24:00.017+07:002011-04-07T03:32:59.789+07:00Полное копирование таблиц Lua<div style="text-align: justify;">Lua - прототипированный язык с динамической типизацией.</div><div style="text-align: justify;">Lua не предоставляет программисту средств для манипулирования указателями и ссылками. В Lua следует различать переменные, исходя из того, что в данный момент времени они содержат. Различают скалярные типы(строки, числовые значения) и не скалярные(таблицы, userdata, функции)</div><a name='more'></a><br />
<ul><li>Примеры работы оператора присваивания со скалярными типами:</li>
</ul><pre class="lua" name="code">local t = 0.5
local p = t
print(t,p)
t = 0.3
print(t,p)
</pre><pre>0.5 0.5
0.3 0.5</pre><div style="text-align: justify;">Аналогично:</div><pre class="lua" name="code">local t = "hello"
local p = t
print(t,p)
t = "bye"
print(t,p)
</pre><pre>hello hello
bye hello</pre><div style="text-align: justify;">Таким образом, если источник оператора присваивания есть переменная скалярного типа то приемник будет содержать копию источника. В нашем случае переменная p содержит копию переменной t.</div><ul><li>Примеры работы оператора присваивания с не скалярными типами:</li>
</ul><pre class="lua" name="code">local t = {"a","b","c"}
local p = t
print(t,p)
for i,val in ipairs(t) do -- покажем все элементы таблицы
print(i,val)
end
t[1] = "W"
print(t,p)
for i,val in ipairs(p) do -- покажем все элементы таблицы
print(i,val)
end
</pre><pre>table: 0x8a93028 table: 0x8a93028
1 a
2 b
3 c
table: 0x8a93028 table: 0x8a93028
1 W
2 b
3 c</pre><div style="text-align: justify;">Таким образом, если источник оператора присваивания есть переменная типа table, userdata или функция(на самом деле переменные содержат лишь адреса в этом случае), то приемник будет содержать адрес того же места памяти, куда ссылался источник. В примере это хорошо видно, содержимое t и p - это адрес 0x8a93028. В силу того, что t и p ссылаются на одно и тоже место в памяти, изменение посредством индексации через t или p не различимо.</div><div style="text-align: justify;">На самом деле подобный механизм удобен и экономичен. Но если программист использует объектно-ориентированный подход в Lua, определяя что таблица - это либо прототип либо объект, то он неизбежно сталкивается с проблемами.</div><pre class="lua" name="code">Foo =
{
p = "hello world",
s = 2,
}
function Foo:Test1()
print(self.p,self.s)
end
local foo1 = Foo -- считаем, что создаем объект прототипа Foo
foo1.s = 100
foo1:Test1()
local foo2 = Foo -- считаем, что создаем объект прототипа Foo
foo2:Test1() -- ожидаем hello world 2
</pre><pre>hello world 100
hello world 100</pre><div style="text-align: justify;">Таким образом, если прототип не является синглетоном, то поведение является не ожидаемым. Программист считает, что он создает объект или хотя бы копию прототипа Foo, но на самом деле он создает переменную, ссылающуюся на прототип.</div><div style="text-align: justify;"><a href="http://lua-users.org/wiki/CopyTable">deepcopy</a> решает эту проблему:</div><pre class="lua" name="code">function deepcopy(object)
local lookup_table = {}
local function _copy(object)
if type(object) ~= "table" then
return object
elseif lookup_table[object] then
return lookup_table[object]
end
local new_table = {}
lookup_table[object] = new_table
for index, value in pairs(object) do
new_table[_copy(index)] = _copy(value)
end
return setmetatable(new_table, _copy(getmetatable(object)))
end
return _copy(object)
end</pre><div style="text-align: justify;">И тогда:</div><pre class="lua" name="code">Foo =
{
p = "hello world",
s = 2,
}
function Foo:Test1()
print(self.p,self.s)
end
local foo1 = deepcopy(Foo)
foo1.s = 100
foo1:Test1()
local foo2 = deepcopy(Foo)
foo2:Test1()
</pre><pre>hello world 100
hello world 2</pre><div style="text-align: justify;">Будет работать ожидаемо.</div><div style="text-align: justify;">Однако в строках 4-5 функции deepcopy:</div><pre class="lua" name="code">if type(object) ~= "table" then
return object</pre><div style="text-align: justify;">мы замечаем, что, если вложенный объект таблицы не является таблицей то копируется его содержимое.</div><pre class="lua" name="code">Foo =
{
p = "hello world",
s = 2,
}
function Foo:Test1()
print(self.p,self.s)
end
local foo1 = deepcopy(Foo)
print(foo1.Test1)
local foo2 = deepcopy(Foo)
print(foo1.Test1)
</pre><pre>function: 0x8613e98
function: 0x8613e98</pre><div style="text-align: justify;">Таким образом в объектах foo1 и foo2 функции Test1 суть одно и тоже. В большинстве случаев этот факт не будет мешать программисту и поведение будет ожидаемым. Но если программист использует такие функции, как setfenv или getfenv, или иные средства изменения функций над функциями таблиц, то поведение вновь становится не ожидаемым.</div><div style="text-align: justify;">Для того чтобы исправить положение, можно использовать следующий модифицированный вариант deepcopy:</div><pre class="lua" name="code">function deepcopymod1(obj)
local lookup_table = {}
local function _copy(object)
if type(object) == "function" then
return loadstring(string.dump(object))
elseif type(object) ~= "table" then
return object
elseif lookup_table[object] then
return lookup_table[object]
end
local new_table = {}
lookup_table[object] = new_table
for index, value in pairs(object) do
new_table[_copy(index)] = _copy(value)
end
return setmetatable(new_table, _copy(getmetatable(object)))
end
return _copy(obj)
end</pre><div style="text-align: justify;">Здесь мы учитываем различие функций таблиц.</div><br />
<div style="text-align: justify;"><span style="font-size: x-large;">Замечание</span></div><div style="text-align: justify;">Стоит обратить внимание, что у string.dump(function) есть <a href="http://www.lua.org/manual/5.1/manual.html">ограничение</a>. А также после loadstring у копии функции будет окружение отличное от окружения исходной функции, нужное окружение необходимо выставить самостоятельно.</div>Unknownnoreply@blogger.com1tag:blogger.com,1999:blog-2259374835286705666.post-59940094611597290262009-07-03T16:41:00.012+07:002011-04-07T03:24:37.341+07:00Смена имени пользователя и домашней директории в Linux<div style="text-align: justify;"><span style="font-size: x-large;">Исходные данные</span></div><div style="text-align: justify;">Имя пользователя : suser</div><div style="text-align: justify;">Домашняя директория : /home/suser</div><br />
<div style="text-align: justify;"><span style="font-size: x-large;">Задача</span></div><div style="text-align: justify;">Сменить <i>suser </i>-> <i>duser</i></div><div style="text-align: justify;">Сменить <i>/home/suser</i> -> <i>/home/duser</i></div><a name='more'></a><br />
<div style="text-align: justify;"><span style="font-size: x-large;">Решение</span></div><pre class="bash" name="code">usermod -l duser suser
mv /home/suser /home/duser
usermod -d /home/duser duser
</pre><div style="text-align: justify;">Однако на этом все только начинается.</div><div style="text-align: justify;">Проблемы:</div><ol><li>Очевидно что система автоматически не изменяет файлы пользователя <i>duser</i>. Имеем дело с устаревшей информацией относительно домашней директории и имени пользователя.</li>
<li>Пользователь <i>duser</i> до сих пор состоит в группе <i>suser </i>(если ранее состоял). Файлы пользователя до сих пор ссылаются на группу <i>suser</i>(если ранее ссылались).<br />
</li>
</ol><div style="text-align: justify;">Первая проблема не имеет универсального решения в силу произвольного содержимого директорий пользователей. Мое мнение, что неплохо бы авторам программ использовать относительные пути и переменные среды в конфигурационных файлах.</div><div style="text-align: justify;">Однако в случае файлов для которых не сверяются контрольные суммы возможно выполнение операции поиска и замены.</div><div style="text-align: justify;">Оцениваем объем проблем:</div><pre class="bash" name="code">grep -ir suser /home/duser/.*
grep -ir /home/suser /home/duser/.*</pre><div style="text-align: justify;">Решение второй проблемы:</div><pre class="bash" name="code">groupadd duser
gpasswd -a duser duser
find / -group suser -exec chown duser:duser '{}' \;
groupdel suser #если необходимо
</pre><span class="Apple-style-span" style="font-size: x-large;">Вывод</span><br />
<div style="text-align: justify;">Лучше не менять имя пользователя и/или домашнюю директорию.</div>Unknownnoreply@blogger.com2