Теория и практика Open vSwitch. Часть 1. Виртуальный распределенный коммутатор Александр Руденко # ovs-vsctl add-br <имя_коммутатора> # ovs-vsctl add-br ovs-sw0 # virt-install --connect qemu:///system --accelerate --hvm --network bridge=ovs-sw0 --name vm01 --ram 512 --cdrom=/iso-store/systemrescuecd-x86-1.6.3.iso --disk=/vm-store/vm01.img # ovs-vsctl add-port <имя_комутатора> <имя_физ.Интерфейса> # ovs-vsctl add-port ovs-sw0 eth0 # ovs-vsctl show # ovs-vsctl list-br # ovs-vsctl list-ports <имя_коммутатора> # ovs-vsctl del-port <имя_коммутатора> <имя_порта> # ovs-vsctl del-br <имя_коммутатора> # ovs-vsctl <команда> <таблица> <запись> <ключ=значение> # ovs-vsctl set port eth0 trunks=10,20,30,40,50 # ovs-vsctl list port # ovs-vsctl list port eth0 # ovs-vsctl find port tag=10 # ovs-vsctl set portс <имя_порта> tag=10 # ovs-vsctl add-br <новый_дочерний_мост> <родительский_мост> # ovs-vsctl add-br sw0-vlan10 ovs-sw0 10 # ovs-vsctl br-to-vlan <имя_коммутатора> # ovs-vsctl br-to-parent <имя_коммутатора> # ovs-vsctl add-bond <имя_коммутатора> <имя_нового_bonda> <список_физ.интерфейсов> # ovs-vsctl add-bond ovs-sw0 bond0 eth0 eth1 # ovs-vsctl set port bond0 lacp=active # ovs-appctl lacp/show # ovs-vsctl add-port ovs-sw0 mgmt0 # ovs-vsctl set interface mgmt0 type=internal # ovs-vsctl set port mgmt0 tag=100 # ifconfig | grep 73:da:7c # ovs-vsctl -- set Bridge ovs-sw0 mirrors=@m -- --id=@mirror0 get Port mirror0 -- --id=@vnet2 get Port vnet2 -- --id=@m create Mirror name=mymirror select-dst-port=@vnet2 select-src-port=@vnet2 output-port=@mirror0 # tcpdump -i mirror0 # ovs-vsctl add-br ovs-sw0 # ovs-vsctl add-port ovs-sw0 eth0 # ovs-vsctl add-port ovs-sw0 tun0 # ovs-vsctl set interface tun0 type=internal # ifconfig tun0 192.168.1.151 netmask 255.255.255.0 # ifconfig tun0 192.168.1.152 netmask 255.255.255.0 # ovs-vsctl add-br is-br0 # ovs-vsctl add-port is-br0 tun1 # ovs-vsctl set interface tun1 type=internal # ifconfig tun1 10.200.10.1 netmask 255.255.255.0 # ifconfig tun1 10.200.10.2 netmask 255.255.255.0 # ovs-vsctl add-port is-br0 gre0 -- set interface gre0 type=gre options:remote_ip=192.168.1.152 # ovs-vsctl add-port is-br0 gre0 -- set interface gre0 type=gre options:remote_ip=192.168.1.151 ----------------------------------------------------------------------------------------------------------------- Новое в Windows Server 2012 R2. Это ответ Microsoft на будущие потребности ИТ Сергей Яремчук PS> Add-WindowsFeature FS-SMBBW PS> Set-SmbBandwidthLimit -Category LiveMigration -BytesPerSecond 1000MB PS> Resize-VirtualDisk -FriendlyName "data_disk" -Size (30GB) PS> Test-NetConnection -Port 80 -InformationLevel Detailed ----------------------------------------------------------------------------------------------------------------- Интернационализация в PHP-фреймворах Константин Чухломин // Файл /application/language/english/domain.php $line['key'] = 'Message'; // Файл контроллера, в теле действия $this->lang->load('domain', 'english'); echo $this->lang->line('key'); // Файл /i18n/fr.php $lang = array( 'key1' => 'message1' 'key2 :var' => 'message2 :var', ); // Файл представления echo __('key1'); echo __('key2 :var', array(':var' => $var)); /app/Locale//LC_MESSAGES/.po // В файле /app/Locale/eng/LC_MESSAGES/default.po описан перевод строки key1 // В файле /app/Locale/eng/LC_MESSAGES/domain.po описан перевод строки key2 echo __('key1'); echo __d('domain', 'key2'); // В файле /app/Locale/eng/LC_MESSAGES/default.po msgid "" msgstr "" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n" "Language-Code: ru\n" "Language-Name: Russian\n" "Preferred-Encodings: utf-8\n" msgid "comment" msgid_plural "comments" msgstr[0] "комментарий" msgstr[1] "комментария" msgstr[2] "комментариев" // В файле представления echo __n('comment', inflector::pluralize('comment'), 12); // 12 комментариев class Post extends AppModel { public $actsAs = array( 'Translate' => array( 'title' => 'titleTranslation' ) ); } $translate = new Zend_Translate( array( 'adapter' => 'gettext', 'content' => '/languages/en/dictionary.en', 'locale' => 'ru' ) ); echo $translate->_('key1'); $locale = new Zend_Locale('de_AT'); $number = Zend_Locale_Format::getNumber( '13.524,678', array( 'locale' => $locale, 'precision' => 3 ) ); print $number; // 13524.678 // Файл /i18n/messages.ru.xml key_1 сообщение_1 key_2 сообщение_2 // допустим, локаль пользователя – ru use_helper('I18N'); echo __('key1'); // сообщение_1 // получение перевода ключа key2 из словаря navigation (/i18n/navigation.ru.xml) echo __('key2', null, 'navigation'); echo format_number_choice( '[0]Nobody is logged|[1]There is 1 person logged|(1,+Inf]There are %1% persons logged', array('%1%' => count_logged()), count_logged() ); use_helper('Number'); echo format_number(12000.10); use_helper('Date'); echo format_date(time()); // Получение метки времени из параметра запроса 'birth_date', сформированного в соответствии с локалью пользователя $timestamp = sfI18N::getTimestampForCulture( $this->getRequestParameter('birth_date'), $this->getUser()->getCulture() ); // В файле /protected/messages/ru/app.php return array( 'Path alias "{alias}" is redefined.' => 'Псевдоним пути переопределен: {alias}.' ); // В файле представления $alias = 'new.path'; echo Yii::t( 'app', // словарь 'Path alias "{alias}" is redefined.', // ключ словаря array( // параметры, передаваемые в сообщение '{alias}' => $alias ) ); // Выведет "Псевдоним пути переопределен: new.path" CREATE TABLE SourceMessage ( id INTEGER PRIMARY KEY, category VARCHAR(32), message TEXT ); CREATE TABLE Message ( id INTEGER, language VARCHAR(16), translation TEXT, PRIMARY KEY (id, language), CONSTRAINT FK_Message_SourceMessage FOREIGN KEY (id) REFERENCES SourceMessage (id) ON DELETE CASCADE ON UPDATE RESTRICT ); // В файле /protected/messages/ru/app.php // (в русском языке 4 множественные формы) return array( '{n} cucumber|{n} cucumbers' => '{n} огурец|{n} огурца|{n} огурцов|{n} огурца', ); // В файле представления Yii::t('app', '{n} cucumber|{n} cucumbers', 1); // 1 огурец Yii::t('app', '{n} cucumber|{n} cucumbers', 1.5); // 1.5 огурца Yii::t('app', '{n} cucumber|{n} cucumbers', 7); // 7 огурцов Yii::t('app', '{n} cucumber|{n} cucumbers', 62); // 62 огурца // 04.08.2013, 22:00:00 echo Yii::app()->dateFormatter->formatDateTime(time()); echo Yii::app()->dateFormatter->formatDateTime(time(), 'short', 'short'); // 04.08.13, 22:00 echo Yii::app()->dateFormatter->formatDateTime(time(), 'long', 'long'); // 4 августа 2013 г., 22:00:00 MSK echo Yii::app()->dateFormatter->formatDateTime(time(), 'full', 'full'); // воскресенье, 4 августа 2013 г., 22:00:00 MSK // 12,35 $ для русской локали и $12.35 для английской // локали echo Yii::app()->numberFormatter->formatCurrency(12.3456, 'USD'); // 20,43 руб. для русской локали и RUB20.43 для английской локали echo Yii::app()->numberFormatter->formatCurrency(20.4287, 'RUB'); ----------------------------------------------------------------------------------------------------------------- Параллельное исполнение запросов в MySQL при разработке сетевых демонов Александр Календарев int mysql_real_query(MYSQL *mysql, const char *query, ulong length) { if (mysql_send_query(mysql,query,length)) return 1; return((int) (*mysql->methods->read_query_result)(mysql)); } typedef struct { ev_io io; // контекст libev int id; // номер исполняемого запроса MYSQL* mysql; // контекст MySQL cmd_func cb; // функция обратного вызова для обработки запроса char * sql; // непосредственно сам запрос fin_func fcb; // функция обратного вызова для обработки окончания запроса } data_t ; void connect_to_mysql(MySQL** mysql) { *mysql = mysql_init(NULL); MYSQL* m = *(mysql); m->free_me = 1; if (mysql_real_connect(*mysql, "localhost", "login", "psw", "dbname", 3306, NULL, 0) == NULL) { printf("can't connect to db %s", mysql_error(*mysql)); mysql_close(*mysql); *mysql = NULL; } else printf("%s:%d connected fd = %d\n", __FILE__, __LINE__, m->net.fd); } context data_t[MAX_CONNECTIONS]; for (i=0; i < MAX_CONNECTIONS; i++) { connect_to_mysql(&context[i].mysql); context[i].id = i; if (!context[i].mysql) exit 1; } typedef struct st_mysql { NET net; /* Communication parameters */ char *host,*user,*passwd,*unix_socket,*server_version, *host_info; char *info, *db; struct charset_info_st *charset; . . . my_bool free_me; /* If free in mysql_close */ my_bool reconnect; /* set to 1 if automatic reconnect */ . . . } MYSQL; typedef struct st_net { Vio *vio; unsigned char *buff,*buff_end,*write_pos,*read_pos; my_socket fd; . . . unsigned char *unused; unsigned int last_errno; void *extension; } NET; int i; for (i=0; i < MAX_CONNECTIONS; i++) { // инициализация события ev_init(&context[i].io, sql_read_cb); // назначение fd ev_io_set(&context[i].io, data[i].mysql->net.fd, EV_READ); // присоединение слушателя события в основной цикл ev_io_start(loop, &data[i].io); } context[0].sql = "SELECT `crc` FROM `users`,`cityes` c WHERE c.`city_id`=164 AND `sex`=1 AND `age` BETWEEN 18 AND 20"; // некоторый запрос 1 context[1].sql = "SELECT `crc` FROM hidden_contacts LIMIT 5"; // некоторый запрос 2 . . . context[0].cb = sql_cb_1; // функция обработчик запроса 1 context[1].cb = sql_cb_2; // функция обработчик запроса 2 static void sql_read_cb(EV_P_ ev_io *w, int revents) { data_t *d = (data_t*)w; // получение номера запроса, на файловом дескрипторе соединения которого только что произошло событие, т.е. запрос исполнился int id = d->id; // получение контекста данных data_t* data = (data_t*)ev_userdata(EV_A); MYSQL* mysql = data[id].mysql; // получение адреса функции обработчика cmd_func cb = data[id].cb; if (0 == mysql_read_query_result(mysql)) { MYSQL_RES *res = mysql_store_result(mysql); printf("a result for m->net.fd = %d\n", (int) mysql->net.fd); cb(EV_A_ res); // вызов функции обработчика mysql_free_result(res); printf("free stor result id=%d\n", id); } else { printf("error in query %f\n", mysql_error(mysql)); } // вызов пост обработчика if(data[id].fcb) data[id].fcb(); } int main(int argc, char** argv) { context data_t[MAX_CONNECTIONS]; /* инициализация контекста */ for (i=0; i < MAX_CONNECTIONS; i++) { connect_to_mysql(&context[i].mysql); context[i].id = i; if (!context[i].mysql) exit 1; } /* назначение функций обработчиков на конкретный запрос */ context[0].sql = "…"; // некоторый запрос 1 context[0].cb = sql_cb_1; // функция обработчик запроса 1 . . . /* инициализация основного цикла событий */ struct ev_loop *loop = ev_default_loop(0); ev_set_userdata(EV_A_ context); /* инициализация слушателей событий и назначение функций обработчиков */ int i; for (i=0; i < MAX_CONNECTIONS; i++) { ev_init(&context[i].io, sql_read_cb); ev_io_set(&context[i].io, data[i].mysql->net.fd, EV_READ); ev_io_start(loop, &data[i].io); } /* инициализация слушателей событий, которая запустится по таймеру через сек 0.001 */ ev_timer timeout_watcher; ev_timer_init (&timeout_watcher, timeout_cb, 0.001, 0.); ev_timer_start (loop, &timeout_watcher); /* запуск цикла отслеживания событий */ ev_run (loop, 0); /* финализация и освобождение цикла событий */ ev_loop_destroy (loop); /* закрытие соединений с БД */ for (i=0; i < MAX_CONNECTIONS; i++) { mysql_close(context[i].mysql); } } static void timeout_cb(EV_P_ ev_timer *w, int revents) { int i; printf("call timer cb ...\n"); data_t* data = (data_t*)ev_userdata(EV_A); for (i=0; i < MAX_CONNECTIONS; i++) { mysql_send_query(data[i].mysql, data[i].sql, strlen(data[i].sql)); printf("send query '%s'\n", data[i].sql); } } static void sql_cb1(EV_P_ MYSQL_RES * res) { MYSQL_ROW *row; printf("%s\n", __FUNCTION__); row = mysql_fetch_row(res); if (row) printf( "query is Ok res=%s\n", row[0] ); else printf( "error query\n"); ev_break(loop, EVBREAK_ONE); } struct mysql_pool_item { MSQL* mysq; // контекст соединения int is_err; // состояние соединения TAILQ_ENTRY(mysql_pool_item) link; // очередь }; TAILQ_HEAD(, mysql_pool_item) mysql_pool = TAILQ_HEAD_INITIALIZER(mysql_pool); // конкретные значения могут тоже задаваться в конфиге #define CNN_COUNT 12 #define THREAD_COUNT 8 // определение структуры рабочего потока typedef struct { pthread_t tid;// системный идентификатор потока int id; // номер потока в пуле struct ev_loop *loop; // libev loop struct ev_io io; // libev io входящего соединения int fd; // fd слушающего сокета } workers_t; struct mysql_pool_item ** pool; // инициализация mysql клиентской библиотеки my_init(); // инициализация mysql клиентской библиотеки mysql_library_init(0, NULL, NULL); // инициализация очереди пула соединений TAILQ_INIT(&mysql_pool); // резервирование памяти для mysql pool pool = malloc(CNN_COUNT, sizeof(void*)); // host, login, passwd, db_name, port определены в конфиге int i; for (i = 0; i < CNN_COUNT; i++) { struct mysql_pool_item * pool_item = (struct mysql_pool_item *) calloc(1, sizeof(mysql_pool_item)); pool[i] = pool_item; // инициализация соединения pool_item->mysql = mysql_init(NULL); // открытие соединения if (mysql_real_connect(pool_item->mysql, host, login, passwd, db_name, port, NULL, 0) ) {; // добавление открытого соединения в пул TAILQ_INSERT_TAIL(&mysql_pool, pool_item, link); } else { printf(«mysql connection error %s» , mysql_error(pool_item->mysql)); mysql_close(pool_item->mysql); // освобождение всех ранее открытых соединений free_pool(&mysql_pool); exit(1); } } // создание пула потоков workers = calloc(THREAD_COUNT, sizeof(workers_t)); for (i = 0; i < THREAD_COUNT; i++) { // назначается файловый дескриптор открытого сокета workers[i].fd = fd; workers[i].id = i; // // инициализируется клиент mysql для потока mysql_thread_init(); // создается непосредственно и сам рабочий поток pthread_create(&workers[i].tid, NULL, worker_listen, (void*)i); } // инициализируем мьютекс соединений static pthread_mutex_t conn_mutex = PTHREAD_MUTEX_INITIALIZER; static struct mysql_pool_item * pool_get() { // запираем соединение мьютексом, чтобы очередь соединений не была доступна другим потокам pthread_mutex_lock(&cnn_mutex); // берем первый элемент из пула соединений static struеct mysql_pool_item * conn = TAILQ_FIRST(&(mysql_pool)); // проверяем на доступность соединения if (!conn) { pthread_mutex_unlock(&cnn_mutex); syslog(LOG_ERR, "Can't pop mysql connection from pool"); return NULL; } if (conn->is_err) { // если данное соединение недоступно, то ищем следующее доступное TAILQ_FOREACH(conn, &(mysql_pool), link) { if( !conn->is_error) break; } } // если последняя коннекция в пуле, то... if(conn == NULL || conn->is_error){ syslog(LOG_ERR, "Can't get mysql connection from pool %s[%d] All connections is disabled.", name,num); pthread_mutex_unlock(&cnn_mutex); return NULL; } // удаляем коннекцию из пула TAILQ_REMOVE(&(mysql_pool), conn, link); // разблокируем очередь pthread_mutex_unlock(&cnn_mutex); // возвращаем полученную из пула коннекцию return conn; } static void pool_put( struct mysql_pool_item * conn ) { if (!conn) { syslog(LOG_ERR, "put_conn: cnn == NULL tid=%X\n", pthread_self()); return; } // запираем соединение мьютексом, чтобы очередь соединений не была доступна другим потокам pthread_mutex_lock(&cnn_mutex); // добавляем коннекцию в начало очереди пула TAILQ_INSERT_HEAD(&(mysql_pool), conn, link); // разблокируем очередь pthread_mutex_unlock(&cnn_mutex); } #define MYSQL_QUERY(c, q) \ ({ \ int ret; \ if ((c) && (c)->is_err == 0) { \ ret = mysql_query((c)->mysql, (q)); \ if (ret != OK) { \ int merrno = mysql_errno((c)->mysql); \ if ( merrno != 2006 && merrno != 2013) \ syslog(LOG_ERR, "MySQL[%ы:%d] ↵ error[%d]: %s query = [%s]\n", \ __FILE__,__LINE__, merrno, ↵ mysql_error((c)->mysql), (q)); \ pthread_mutex_lock(&conn_mutex); \ // устанавливаем флаг в 1 – коннекция с ошибкой \ (c)->is_err = 1; \ pthread_mutex_unlock(&conn_mutex); \ } \ } \ ret; \ }) // вызывается из основного потока по таймеру, проверяет состояние коннекций int mysql_connection_check() { int i; // просматриваем все доступные соединения for (i = 0; i < CNN_SIZE; i++) { struct mysql_pool_item *conn = (mysql_pool_item *)&pool[i]; // запираем мьютексом доступ к соединению pthread_mutex_lock(&conn_mutex); // проверяем состояние соединения if (!conn->is_err) { // соединение нормальное, отпираем и переходим к следующему pthread_mutex_unlock(&conn_mutex); continue; } // закрываем соединение и освобождаем память mysql_close(conn->mysql); // инициализируем соединение conn->mysql = mysql_init(NULL); // если нужно, то устанавливаем необходимые опции mysql_set_options(conn->mysql); // открываем соединение MYSQL* res = mysql_real_connect(conn->mysql, host, login, passwd, db_name, port, NULL, 0); if (res == NULL) { syslog( LOG_ERR, "MySQL reconnect error[%d] %s\n", mysql_errno(conn->mysql), mysql_error(conn->mysql)); pthread_mutex_unlock(&conn_mutex); // переходим к следующему соединению continue; } // сбрасываем флаг ошибки соединения conn->is_err = 0; // отпираем мьютексом доступ к соединению pthread_mutex_unlock(&conn_mutex); // конец цикла, переходим к следующему соединению } return 0; } mysqli::poll ( array &$read , array &$error , array &$reject , int $sec [, int $usec ] ); $reader_hosts = array( "mysqlhost1", "mysqlhost2", "mysqlhost3", "mysqlhost4", "mysqlhost5" ); $all_links=array(); foreach ($reader_hosts as $i) { $mysqli = new mysqli($i, 'root', '', 'test'); if ($mysqli->connect_error) { echo 'Connect Error (', $mysqli->connect_errno , ') ', $mysqli->connect_error; } else { $all_links[]=$mysqli; } } foreach ($all_links as $linkid => $link) { //запускаем запросы асинхронно $link->query("SELECT `lname`, `name`, age FROM `profile` WHERE name = '$name'", MYSQLI_ASYNC); } $processed = 0; $res = array(); do { $links = $errors = $reject = array(); // формируем массивы входящих параметров foreach ($all_links as $link) { $links[] = $errors[] = $reject[] = $link; } // делаем опрос MySQL дескрипторов if (!mysqli_poll($links, $errors, $reject, 5)) { continue; } // извлечение результата foreach ($links as $k=>$link) { if ($result = $link->reap_async_query()) { // данные собираем в общий массив $res[] = $result->fetch_row(); mysqli_free_result($result); } else { die(sprintf("MySQLi Error: %s", mysqli_error($link))); } } $processed++; } while ($processed < count($all_links)); // печать результата print_r($res); ----------------------------------------------------------------------------------------------------------------- Арифметические выражения: анатомия, разбор, программирование Алексей Вторников Арифметические выражения (функция eval ()) import javax.script.ScriptEngineManager; import javax.script.ScriptEngine; public class Test { public static void main (String[] args) { ScriptEngineManager mgr = new ScriptEngineManager (); ScriptEngine engine = mgr.getEngineByName ("JavaScript"); try { System.out.println(engine.eval ("(40 + 2) * 5")); } catch (Exception ex) { ex.printStackTrace (); } } } // Описание токена (лексемы) import java.util.*; public class Token { private String tToken; private int tType, tPrio; public Token (String tToken, int tType, int tPrio) { this.tToken = tToken; this.tType = tType; this.tPrio = tPrio; } // Методы доступа к элементам структуры public String getToken () {return tToken;} public int getType () {return tType;} public int getPrio () {return tPrio;} } List tokensList; public class ParseString { // Выделить операцию private Token parseOperator (char symbol) { switch (symbol) { ... } } // Выделить цифру private Token parseDigit (char [] exp, int position, int size) { ... } // Выделить переменную private Token parseVariable (char[] exp, int position, int size) { ... } // Разбор строки на составляющие public List parse (char [] expression) { List result = new ArrayList (); int currPosition = 0, expLength = expression.length; while (currPosition < expLength) { Token nextToken = null; nextToken = parseOperator (expression [currPosition]); if (nextToken == null) { nextToken = parseDigit (expression, currPosition, expLength); } if (nextToken == null) { nextToken = parseVariable (expression, currPosition, expLength); } if (nextToken == null) { nextToken = new Token ("?", UNKNOWN, -1); } result.add (nextToken); currPosition += nextToken.getToken ().length (); } return result; } // Типы лексем final static int ADD_OP = 0; final static int SUB_OP = 1; final static int MUL_OP = 2; final static int DIV_OP = 3; final static int LPAREN = 100; final static int RPAREN = 101; final static int VARIABLE = 200; final static int DIGIT = 201; final static int UNKNOWN = -1; } // Обработка элементов постфиксной записи private void calculate () { EvaluatorStack s = new EvaluatorStack (); double result = 0.00, x = 0.00, y = 0.00; s.push (result); // Составляющие постфиксной записи String [] args = exp.split (" "); try { for (int i = 0; i < args.length; i++) { if (args [i].equals ("+")) { x = s.pop (); y = s.pop (); result = x + y; s.push (result); continue; } if (args [i].equals ("-")) { x = s.pop (); y = s.pop (); result = y - x; s.push (result); continue; } if (args [i].equals ("*")) { x = s.pop (); y = s.pop (); result = x * y; s.push (result); continue; } if (args [i].equals ("/")) { x = s.pop (); y = s.pop (); result = y / x; s.push (result); continue; } try { // Если число – втолкнуть его в стек s.push (Double.parseDouble (args [i])); } catch (NumberFormatException nfe) { // Если переменная – получить значение переменной s.push (readDouble (args [i])); } } } catch (Exception ex) { // Ошибка исполнения System.out.println ("Rest in peace"); return; } System.out.println (result); } ----------------------------------------------------------------------------------------------------------------- Инструментарий разработчика C# Михаил Ушаков csc.exe /r:MyLibrary.dll /t:exe /out:MyConsoleProgram.exe Program.cs Parser.cs Features.cs csc.exe @MyProperties.rsp /out: MyConsoleProgram /t:exe /lib:C:\MyLibs C:\Program Files\MyCoreLibs /r:MyLibrary.dll /linkresource:MyResources.resx /optimize Program.cs Parser.cs Features.cs cd C:\tomcat\apache-tomcat-7.0.42\bin service.bat install service.bat install MyTomcat -----------------------------------------------------------------------------------------------------------------