Последние записи
- Преобразовать массив байт в вещественное число (single)
- TChromium (CEF3), сохранение изображений
- Как в Delphi XE обнулить таймер?
- Изменить цвет шрифта TextBox на форме
- Ресайз PNG без потери прозрачности
- Вывод на печать графического файла
- Взаимодействие через командную строку
- Перенести программу из Delphi в Lazarus
- Определить текущую ОС
- Автоматическая смена языка (раскладки клавиатуры)
Интенсив по Python: Работа с API и фреймворками 24-26 ИЮНЯ 2022. Знаете Python, но хотите расширить свои навыки?
Slurm подготовили для вас особенный продукт! Оставить заявку по ссылке - https://slurm.club/3MeqNEk
Online-курс Java с оплатой после трудоустройства. Каждый выпускник получает предложение о работе
И зарплату на 30% выше ожидаемой, подробнее на сайте академии, ссылка - ttps://clck.ru/fCrQw
23rd
Фев
Работа с NetLink в Linux. Часть 1
И вновь, после вынужденного долгого перерыва приветствую уважаемых читателей нашего журнала. За время этого перерыва накопилось достаточно много интересных материалов, которые я постараюсь осветить в новых статьях. Начну с этой. Надеюсь, что эта информация кому-то поможет и пригодится, как, в свое время, помогла мне. В прошлой статье я рассказал о системном вызове IOCTL и обещал рассказать о Netlink. Пришло время выполнять обещание 🙂 К сожалению, невозможно изложить все в рамках одной журнальной статьи. Поэтому материал я разбил на несколько частей. Настоящая статья – первая часть.
Олег Кутков
by Oleg Kutkov elenbert@gmail.com
Итак, Netlink представляет из себя особый компонент Linux ядра. С ним можно общаться через обычный сокет передавая и принимая сообщения, сформированные особым образом. Что же дает нам netlink?
С помощью Netlink мы можем:
- получать уведомления об изменении сетевых интерфейсов (название изменившегося интерфейса и что именно произошло), таблиц маршрутизации, файрволла;
- управлять всеми таблицами маршрутизации;
- управлять параметрами сетевых интерфейсов;
- управлять параметрами файрволла;
- управлять arp таблицей;
- реализовать взаимодействие со своим модулем в ядре.
Кроме этого Netlink позволяет нам отказаться от устаревшего вызова ioctl, упростить и унифицировать код, так как все операции выполняются посредством стандартного сокета. Гарантируется быстрая работа кода и его общая надежность. Именно Netlink использует мощная утилита администрирования iproute2, пришедшая на смену утилитам ifconfig, route и другие.
К сожалению о Netlink очень мало материалов как на русском языке, так и на английском. Пожалуй, наиболее ценным источником можно считать две статьи в Linux journal и Doxygen- документацию. В этой статье я постараюсь приподнять завесу тайны и рассказать как можно больше.
Архитектура Netlink
Как уже говорилось выше, Netlink – это особый компонент ядра, с которым пользователь может общаться посредством обычных сокетов. Для работы с netlink существует особое семейство протоколов – AF_NETLINK, его необходимо указывать при создании нового сокета. Тип сокета следует выбирать SOCK_RAW или SOCK_DGRAM, для протокола, в данном случае, нет разницы. Как можно догадаться, протокол netlinka является диаграммным и не гарантирует доставку сообщений, хоть и старается это сделать всеми доступными средствами.
Каждое сообщение netlink представляет собой поток байт, содержащий один или несколько заголовков, представленных структурой nlmsghdr, а так же связанных с ними данными, которые называются «полезной нагрузкой» (playload). Сообщение, во время доставки, может быть разбито на несколько частей. В таких случаях каждый следующий пакет помечается флагом NLM_F_MULTI, а последний флагом NLMSG_DONE. Для разбора сообщений имеется целый набор макросов, определенный в заголовочном файле netlink.h. Там же определено все прочее, связанное с Netlink.
Надо сказать, что существует отдельная библиотека для работы с netlink – libnl. Она реализует особый уровень абстракции над netlink сокетами и предоставляет множество методов. Лично мне она не очень нравится, т.к. немного имеет немного запутанный и плохо документированный API, который любит часто меняться, что требует изменений и в приложениях, использующих эту библиотеку. Я один раз напоролся на такой сюрприз, поэтому мы не будет рассматривать эту библиотеку, а реализуем весь протокол сами, увидите, это не очень сложно.
Создание сокета netlink
Объявление netlink сокета выглядит так:
Где:
AF_NETLINK – протокол Netlink,
SOCK_RAW – тип сокета,
NETLINK_ROUTE – семейство Netlink протокола
Последний параметр может быть различным, в зависимости от того, что мы именно хотим получить от Netlink.
Приведу таблицу со наиболее интересными параметрами (полный список параметров можно посмотреть в документации):
- NETLINK_ROUTE – получать уведомления об изменениях таблицы маршрутизации и сетевых интерфейсов (так же может использоваться для изменения всех параметров вышеперечисленных объектов).
- NETLINK_USERSOCK – зарезервировано для определения пользовательских протоколов.
- NETLINK_FIREWALL – служит для передачи IPv4 пакетов из сетевого фильтра на пользовательский уровень
- NETLINK_INET_DIAG – мониторинг inet сокетов
- NETLINK_NFLOG – ULOG сетевого/пакетного фильтра
- NETLINK_SELINUX – получать уведомления от системы Selinux
- NETLINK_NETFILTER – работа с подсистемой сетевого фильтра
- NETLINK_KOBJECT_UEVENT – получение сообщений ядра
Далее созданные сокет можно использовать для отправки сообщений, с помощью стандартной функции send и приема сообщений с помощью recvmsg.
Сообщения Netlink
Как уже выше сообщалось – каждое netlink сообщение представлено одним или несколькими заголовками, за которыми следуют полезные данные. Заголовок сообщения представлен структурой nlmsghdr:
{
__u32 nlmsg_len; // размер сообщения, с учетом заголовка
__u16 nlmsg_type; // тип содержимого сообщения (об этом ниже)
__u16 nlmsg_flags; // различные флаги сообщения
__u32 nlmsg_seq; // порядковый номер сообщения
__u32 nlmsg_pid; // идентификатор процесса (PID), отославшего сообщение
};Syhi-подсветка кода
Поле nlmsg_type может указывать на один из стандартных типов сообщений:
NLMSG_NOOP – сообщения такого типа игнорируются.
NLMSG_ERROR – сообщение с ошибкой, и в секции полезных данных будет структура nlmsgerr (о ней чуть ниже)
NLMSG_DONE – сообщение с этим флагом должно завершать сообщение, разбитое на несколько частей
Структура nlmsgerr:
{
int error; // отрицательное значение кода ошибки
struct nlmsghdr msg; // заголовок сообщения, связанного с ошибкой
};Syhi-подсветка кода
Сообщения могут быть одного или нескольких (различные типы объеденяются с помощью операции логического или – |) типов:
- NLM_F_REQUEST – сообщение – запрос чего либо
- NLM_F_MULTI – сообщение, часть сообщения разбитого на части
- NLM_F_ACK – сообщение – запрос подтверждения
- NLM_F_ECHO – эхо запрос. обычное направление – запросы из уровня ядра на пользовательский уровень
- NLM_F_ROOT – данный тип запроса возвращает некую таблицу, внутри некой сущности
- NLM_F_MATCH – запрос возвращает все найденные соответствия
- NLM_F_ATOMIC – возвращает атомарный срез некой таблицы
- NLM_F_DUMP – аналог NLM_F_ROOT|NLM_F_MATCH
Дополнительные флаги:
- NLM_F_REPLACE – заменить существующий аналогичный объект
- NLM_F_EXCL – не заменять, если такой объект уже существует
- NLM_F_CREATE – создать объект, если он не существует
- NLM_F_APPEND – добавить объект в список к уже существующему
Для идентификации клиентов (на уровне ядра и на пользовательском уровне) существует специальная адресная структура – nladdr:
{
sa_family_t nl_family; // семейство протоколов – всегда AF_NETLINK
unsigned short nl_pad; // поле всегда заполнено нулями
pid_t nl_pid; // идентификатор процесса
__u32 nl_groups; // маска групп получателей/отправителей
};Syhi-подсветка кода
nl_pid – это уникальный адрес сокета. Для клиентов в ядре он всегда равен нулю. Для клиентов на пользовательском уровне он равен идентификатору процесса, владеющего сокетом. Каждый идентификатор должен быть уникальным, поэтому тут вы можете натолкнутся на проблему, когда попытаетесь создать несколько netlink сокетов в многопоточном приложении: при создании нового сокета будет возвращаться ошибка «Operation not permitted».
Для обхода данного ограничения следуют nl_pid присваивать значение данного выражения:
Присваивать значение идентификатора следует до того, как будет вызван bind() для сокета.
Так же идентификатору можно присвоить нулевое значение. В этом случае генерацией уникальных идентификаторов будет заниматься ядро, но первому сокету созданному в приложение всегда будет присваиваться значение идентификатора данного приложения.
nl_groups – это битовая маска, каждый бит которой представляет номер группы netlink. При вызове bind() для сокета netlink следует указывать битовую маску группы, которую желает прослушивать приложение, в данном контексте. Различные группы могут быть объединены с помощью логического или – |
Основные группы определены в заголовочном файле netlink. Пример некоторых из них:
- RTMGRP_LINK – эта группа получает уведомления об изменениях в сетевых интерфейсах (интерфейс удалился, добавился, опустился, поднялся)
- RTMGRP_IPV4_IFADDR – эта группа получает уведомления об изменениях в IPv4 адресах интерфейсов (адрес был добавлен или удален)
- RTMGRP_IPV6_IFADDR – эта группа получает уведомления об изменениях в IPv6 адресах интерфейсов (адрес был добавлен или удален)
- RTMGRP_IPV4_ROUTE – эта группа получает уведомления об изменениях в таблице маршрутизации для IPv4 адресов
- RTMGRP_IPV6_ROUTE – эта группа получает уведомления об изменениях в таблице маршрутизации для IPv6 адресов
После структуры заголовка nlmsghdr всегда расположен указатель на блок данных. Доступ к нему можно получить с помощью макросов, о которых будет рассказано далее.
Макросы Netlink
- NLMSG_ALIGN – округляет размер сообщения netlink до ближайшего большего значения, выровненного по границе.
- NLMSG_LENGTH – принимает в качестве параметра размер поля данных (payload) и возвращает выровненное по границе значение размера для записи в поле nlmsg_len заголовка nlmsghdr.
- NLMSG_SPACE – возвращает размер, который займут данные указанной длины в пакете netlink.
- NLMSG_DATA – возвращает указатель на данные, связанные с переданным заголовком nlmsghdr.
- NLMSG_NEXT – возвращает следующую часть сообщения, состоящего из множества частей. Макрос принимает следующий заголовок nlmsghdr в сообщении, состоящем из множества частей. Вызывающее приложение должно проверить наличие в текущем заголовке nlmsghdr флага.
- NLMSG_DONE – функция не возвращает значение NULL при завершении обработки сообщения. Второй параметр задает размер оставшейся части буфера сообщения. Макрос уменьшает это значение на размер заголовка сообщения.
- NLMSG_OK – возвращает значение TRUE (1), если сообщение не было усечено и его разборка прошла успешно.
- NLMSG_PAYLOAD – возвращает размер данных (payload), связанных с заголовком nlmsghdr.
От теории к практике
Ну что же. Думаю, что я достаточно помучал Вас теорией 🙂 Может быть что-то показалось запутанным или не понятным – постараюсь разжевать все в наглядных примерах, там на самом деле нет ничего сложного.
Итак, давайте-ка напишем небольшое приложение, которое будет получать уведомления об изменениях в сетевых интерфейсах и таблице маршрутизации! Ниже будет введен целый ряд новых структур и объектов, я о них обязательно расскажу, но обо всем по порядку… Исходный код (монитор интерфейсов):
* monitor.c
* мониторинг сетевых интерфейсов и таблицы маршрутизации
*/
#include <errno.h>
#include <stdio.h>
#include <memory.h>
#include <net/if.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <linux/rtnetlink.h>
// небольшая вспомогательная функция, которая с помощью макрасов netlink разбирает сообщение и
// помещает блоки данных в массив атрибутов rtattr
void parseRtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len)
{
memset(tb, 0, sizeof(struct rtattr *) * (max + 1));
while (RTA_OK(rta, len)) { // пока сообщение не закончилось
if (rta–>rta_type <= max) {
tb[rta–>rta_type] = rta; //читаем атрибут
}
rta = RTA_NEXT(rta,len); // получаем следующий атрибут
}
}
int main()
{
int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); // создаем нужный сокет
if (fd < 0) {
printf("Ошибка создания netlink сокета: %s", (char*)strerror(errno));
return 1;
}
struct sockaddr_nl local; // локальный адрес
char buf[8192]; // буфер сообщения
struct iovec iov; // структура сообщения
iov.iov_base = buf; // указываем buf в качестве буфера сообщения для iov
iov.iov_len = sizeof(buf); // указываем размер буфера
memset(&local, 0, sizeof(local)); // очищаем структуру
local.nl_family = AF_NETLINK; // указываем семейство протокола
local.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV4_ROUTE; // указываем необходимые группы
local.nl_pid = getpid(); //в качестве идентификатора указываем идентификатор данного приложения
// структура сообщения netlink – инициализируем все поля
struct msghdr msg;
{
msg.msg_name = &local; // задаем имя – структуру локального адреса
msg.msg_namelen = sizeof(local); // указываем размер
msg.msg_iov = &iov; // указываем вектор данных сообщения
msg.msg_iovlen = 1; // задаем длину вектора
}
if (bind(fd, (struct sockaddr*)&local, sizeof(local)) < 0) { // связываемся с сокетом
printf("Ошибка связывания с netlink сокетом: %s", (char*)strerror(errno));
close(fd);
return 1;
}
// читаем и разбираем сообщения из сокета
while (1) {
ssize_t status = recvmsg(fd, &msg, MSG_DONTWAIT); // после вызова bind() мы можем принимать сообщения для указанных групп
// небольшие проверки
if (status < 0) {
if (errno == EINTR || errno == EAGAIN)
{
usleep(250000);
continue;
}
printf("Ошибка связывания приема сообщения netlink: %s", (char*)strerror(errno));
continue;
}
if (msg.msg_namelen != sizeof(local)) { //провряем длину адреса, мало ли 🙂
printf("Некорректная длина адреса отправителя");
continue;
}
// собственно разбор сообщения
struct nlmsghdr *h; // указатель на заголовок сообщения
for (h = (struct nlmsghdr*)buf; status >= (ssize_t)sizeof(*h); ) { // для всех заголовков сообщений
int len = h–>nlmsg_len; // длина всего блока
int l = len – sizeof(*h); // длина текущего сообщения
char *ifName; // имя соединения
if ((l < 0) || (len > status)) {
printf("Некорректная длина сообщения: %i", len);
continue;
}
// смотрим тип сообщения
if ((h–>nlmsg_type == RTM_NEWROUTE) || (h–>nlmsg_type == RTM_DELROUTE)) { // если это изменения роутов – печатаем сообщение
printf("Произошли изменения в таблице маршрутизации\n");
} else { // в остальных случаях начинаем более детально разбираться
char *ifUpp; // состояние устройства
char *ifRunn; // состояние соединения
struct ifinfomsg *ifi; // указатель на структуру, содержащюю данные о сетевом подключении
struct rtattr *tb[IFLA_MAX + 1]; // массив атрибутов соединения, IFLA_MAX определен в rtnetlink.h
ifi = (struct ifinfomsg*) NLMSG_DATA(h); // получаем информацию о сетевом соединении в кором произошли изменения
parseRtattr(tb, IFLA_MAX, IFLA_RTA(ifi), h–>nlmsg_len); // получаем атрибуты сетевого соединения
if (tb[IFLA_IFNAME]) { // проверяем валидность атрибута, хранящего имя соединения
ifName = (char*)RTA_DATA(tb[IFLA_IFNAME]); //получаем имя соединения
}
if (ifi–>ifi_flags & IFF_UP) { // получаем состояние флага UP для соединения
ifUpp = (char*)"UP";
} else {
ifUpp = (char*)"DOWN";
}
if (ifi–>ifi_flags & IFF_RUNNING) { // получаем состояние флага RUNNING для соединения
ifRunn = (char*)"RUNNING";
} else {
ifRunn = (char*)"NOT RUNNING";
}
char ifAddress[256]; // сетевой адрес интерфейса
struct ifaddrmsg *ifa; // указатель на структуру содержащую данные о сетевом интерфейсе
struct rtattr *tba[IFA_MAX+1]; // массив атрибутов адреса
ifa = (struct ifaddrmsg*)NLMSG_DATA(h); // получаем данные из соединения
parseRtattr(tba, IFA_MAX, IFA_RTA(ifa), h–>nlmsg_len); // получаем атрибуты сетевого соединения
if (tba[IFA_LOCAL]) { // проверяем валидность указателя локального адреса
inet_ntop(AF_INET, RTA_DATA(tba[IFA_LOCAL]), ifAddress, sizeof(ifAddress)); // получаем IP адрес
}
switch (h–>nlmsg_type) { //что конкретно произошло
case RTM_DELADDR:
printf("Был удален адрес интерфейса %s\n", ifName);
break;
case RTM_DELLINK:
printf("Был удален интерфейс %s\n", ifName);
break;
case RTM_NEWLINK:
printf("Новый интерфейс %s, состояние интерфейса %s %s\n", ifName, ifUpp, ifRunn);
break;
case RTM_NEWADDR:
printf("Был добавлен адрес для интерфейса %s: %s\n", ifName, ifAddress);
break;
}
}
status –= NLMSG_ALIGN(len); // выравниваемся по длине сообщения (если этого не сделать – будет очень плохо, можете проверить :))
h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len)); //получаем следующее сообщение
}
usleep(250000); // немножко спим, что бы не очень грузить процессор
}
close(fd); // закрываем дескриптор сокета
return 0;
}Syhi-подсветка кода
Компиляция: gcc monitor.c –o monitor. Запуск: <img1.png>
Пояснения к программе
Как обещал – появились новые структуры. Давайте рассмотрим их, а так же более детально логику приложения:
{
void *iov_base; // буфер данных
__kernel_size_t iov_len; // размер данных
};Syhi-подсветка кода
Эта структура служит хранилищем полезных данных, передаваемых через сокеты netlink. Полю iov_base присваивается указатель на байтовый массив. Именно в этот байтовый массив будут записаны данные сообщения:
void *msg_name; // адрес клиента (имя сокета)
int msg_namelen; // длина адреса
struct iovec *msg_iov; // указатель на блок данных
__kernel_size_t msg_iovlen; // количество блоков данных
void *msg_control; // магическое число для протокола, не используется в данных случаях
__kernel_size_t msg_controllen; // длина предидущего поля данных
unsigned msg_flags; // флаги сообщения
};Syhi-подсветка кода
Эта структура непосредственно передается через сокет. Она содержит в себе указатель на блок полезных данных, количество данных блоков, а так же ряд дополнительных флагов и полей, пришедших, по большей части, с платформы BSD:
{
unsigned char ifi_family; // семейство (AF_UNSPEC)
unsigned short ifi_type; // тип устройства
int ifi_index; // индекс интерфейса
unsigned int ifi_flags; // флаги устройства
unsigned int ifi_change; // маска смены, зарезервировано для использования в будущем и всегда должно быть равно 0xFFFFFFFF
};Syhi-подсветка кода
Эта структура используется для представления сетевого устройства, его семейства, типа, индекса и флагов:
{
unsigned char ifa_family; // Тип адреса (AF_INET или AF_INET6)
unsigned char ifa_prefixlen; // Длина префикса адреса (длина сетевой маски)
unsigned char ifa_flags; // Флаги адреса
unsigned char ifa_scope; // Область адреса
int ifa_index; // Индекс интерфейса, равен аналогичному полю в ifinfomsg
};Syhi-подсветка кода
Эта структура служит для представления сетевого адреса, назначенного на сетевой интерфейс:
{
unsigned short rta_len; // Длина опции
unsigned short rta_type; // Тип опции
/* данные */
}Syhi-подсветка кода
Эта структура* служит для хранения, какого–либо параметра соединения или адреса.
Пояснения к коду
После запуска программы мы создаем netlink сокет и проверяем успешность его создания. Далее происходит объявление необходимых переменных и заполнение структуры локального адреса. Тут мы указываем группы сообщений, на которые хотим подписаться: RTMGRP_LINK, RTMGRP_IPV4_IFADDR, RTMGRP_IPV4_ROUTE.
Так же объявляем структуру сообщения и связываем с ней один блок данных. После этого происходит связывание с сокетом, с помощью bind(). После этого мы становимся подписанными на сообщения для указанных групп. Можно принимать сообщения через сокет.
Далее следует бесконечный цикл приема сообщений из сокета. Так как принимаемый блок данных может иметь несколько заголовков и ассоциированных с ними данных – начинаем перебирать, с помощью netlink макросов все принятые данные. Каждое новое сообщение расположено по указателю struct nlmsghdr *h. Теперь можно разбирать собственно сообщение. Смотрим на поле nlmsg_type и выясняем, что же за сообщение к нам приехало. Если оно связано с таблицей маршрутизации – печатаем сообщение и идем к следующему сообщению. А если нет – начинаем детально разбираться.
Объявляются массивы опций rtattr, куда будут складываться все необходимые данные. За получение этих данных отвечает вспомогательная функция parseRtattr. Она использует макросы Netlink и заполняет указанный массив всеми атрибутами из блока данных структуры ifinfomsg или ifaddrmsg.
После того как мы получили массивы, заполненные атрибутами – можем работать с этим значениями, анализировать их, печатать. Доступ к каждому атрибуту осуществляется по его индексу. Все индексы определены в заголовочных файлах netlink и прокомментированы. В данном случае мы используем следующие индексы:
- IFLA_IFNAME – индекс атрибута с именем интерфейса.
- IFA_LOCAL – индекс атрибута с локальным IP адресом.
После всего этого мы обладаем полной информацией о том, что произошло и можем печатать информацию на экран. Как видите ничего страшного.
Заключение
Итак, мы получили базовые знания о Netlink и его протоколе, рассмотрели все основные структуры и написали наглядный пример. На этом я бы хотел завершить первую часть. Надюсь, что Вам понравилось. Если есть, какие то вопросы – с удовольствием отвечу.
В следующей части мы изучим работу с таблицей маршрутизации и научимся выполнять специализированные запросы. Будет изучено еще ряд структур и методик. Так же будет рассмотрен пример определения своего собственного протокола и общение, с помощью него, со своим модулем в ядре. Для тех, кому нетерпимость изучить что-то новое и кто умеет и хочет учиться чему-то самостоятельно, думаю, пригодится полный RFC протокола Netlink [1], а так же статья в Linux journal [2]. До встречи на страницах журнала клуба ПРОграммистов!
Продолжение следует…
Литература
- RFC-3549 Linux Netlink as an IP Services Protocol http://tools.ietf.org/html/rfc3549
- Kernel Korner — Why and How to Use Netlink Socket http://www.linuxjournal.com/article/7356
Статья из восьмого выпуска журнала «ПРОграммист».
23rd
WMI. Wладение Mагической Iнформацией. часть 1
Чего тут писать-то? Про то, как вы думаете, что нужно хорошему системному администратору? «Монога» пива? Ну, это тоже не плохо бы. Но, что еще? Чтобы пользователи никогда не звонили? Ну, а если среди них симпатичные девчонки, к которым приятно нагрянуть для «осмотра» их компушек? Нет, все это отмазки. Хороший администратор должен иметь в своем арсенале как можно больше инструментов управления подчиненной ему сети. Конечно, такие инструменты существуют и давно, кстати, но большинство из них требуют наличие установленного клиента на целевой машине, а это не всегда кошерно. Можно, но вдруг пользователь запротестует: «…нечего мне на винду ставить хлам». К тому же, не все из этих инструментов бесплатные. Что же делать в таких случаях, когда и нужно и никак? Ответ прост – написать инструмент самому, но на своих условиях игры.
Виталий Белик
by Stilet www.programmersforum.ru
Как бы не ругали Виндоус – это все таки могучая операционная система, и если уметь владеть ей в полной мере, она станет надежнее плохо настроенного (из-за незнания) Линукса, к примеру. Я веду к тому, что в винде уже встроены механизмы удаленного администрирования. Например, это «Удаленный рабочий стол». Не Радмин, но уже неплохо – стандартный. А мы сегодня поговорим о другом механизме. О механизме, позволяющем пройтись по операционке аки по таблицам базы данных – это WMI…
Мы начинаем КВН
Итак, предлагаю начать с того момента, где упоминается эта самая технология. Что это, почему это, что она может? Заглянем в Википедию [1]. Это инструментарий управления Виндоусом. Уже обнадеживает. Три эти слова подразумевают возможность контроля за происходящим в операционной системе, это как раз то, что нужно хорошему администратору.
Читаем дальше: «WMI* представляет собой набор классов, содержащих свойства, доступ к которым открывает информацию о компьютере, операционной системе, их параметрах и установках». Особенную важность уделили динамическому изменению этой информации, поэтому доступ к ней происходит посредством запросов. Получается, что Виндовс выступает в роли системы управления базой данных, откуда мы эту информацию и запрашиваем. Ну, а раз это база данных, согласитесь, было бы просто замечательно, если-бы работа с ней велась на языке, привычном для многих (если не всех) СУБД – SQL.
Читаем дальше. Опа! Угадали. То ли мы верно догадались, то ли Микрософт просчитал заранее удобство… В любом случае, для получения информации используется язык запросов WQL, являющийся одной из разновидностью SQL. Вот это уже радует. Зная язык SQL, трудно будет запутаться при запросах WMI. Это облегчает задачу, еще и потому что запрос сам по себе является строкой, и может быть введен самим пользователем программы непосредственно во время работы самой программы.
Отличненько. По логике в SQL описывается тот или иной доступ к таблицам и объектам базы данных, но в случае с СУБД программисту известно о том, к каким объектам в БД у него есть доступ, который ставит админ базы данных, либо же сам программист знает, какими финтами обладает его база данных. Как же быть в случае с WMI? Кто скажет: какова структура таблиц? Ой, пардон, речь-то шла о классах… какова структура классов WMI и вообще что они из себя представляют?
Вот эту информацию можно почерпнуть в MSDN [2]. Кстати, счастливые обладатели Visual Studio с MSDN-ом, могут набрать в хелпе в поиске WMI, и получить ту же информацию. Ну, предположим, у нас нет такого хелпа (хотя я лично для себя запасся им), и нам придется напрягать интернет. Ничего. Не так сложно выйдя по ссылке [2], попасть на одну из ветвей, описывающих все это благо, с названием «WMI Service Management Classes», а уж на ней можно выйти на WMI Classes, где собственно описаны все классы, которые можно получить запросом**.
Ну, да ладно. Билл им судья. Я на всякий случай приведу ссылку, по которой точнее можно выйти на список классов [3]. Не знаю, что с ней случится через год-два, но на момент написания статьи она была актуальна.
Давайте посмотрим, что за информацию нам предлагает Микрософт…430 классов.… Не кисло. Такое впечатление, что о системе, о ее состоянии на момент запроса можно узнать все-все. Это хорошо. Такая информация для администратора иногда оказывается очень ценной.
Ладно. Что может эта загогулина мы теперь знаем. А как получить эти самые классы? То есть объекты, или что там у них… интерфейсы?
Вот тут немного придется мыслить неравномерно. Думаю, многим привычно, что указатели на объекты возвращают конструкторы класса. Однако, в случае с WMI это не совсем так. Здесь бал правит особый провайдер (все-таки получение информации происходит путем взаимодействия с операционкой через язык запросом, а стало быть, должен быть «черный ящичек», обрабатывающий эти запросы) – WBEM. Механизм этот представляет СОМ сервер, который можно использовать в своих программах, предварительно заполучив его инстанцию. Это делается стандартными методами загрузки СОМ сервера. Например, в Делфи это функция CreateOleObject, которая вызывает CoCreateInstance из библиотеки ole32.dll, и возвращает указатель на объект-провайдер, от которого, скормив ему WQL запрос, можно получить указатели на WMI классы.
Его механизм открывает набор-энумератор. Таким образом, проходя в цикле по динамическому списку, получаем указатели на классы, которые и содержат информацию по запросу. Вообще, если проводить аналогию с базами данных очень легко представить класс WMI как плоскую таблицу, где имена свойств – это имена полей, а их содержимое… нет, не записи, а запись. Один экземпляр класса – одна запись. Таким образом, получается два цикла: один перечисляет список объектов, запрошенного класса, а второй перечисляет поля объекта с информацией.
Что еще нужно знать о WMI? То, что эта технология позволяет не только получать информацию о системе, но и вызывать команды управления системой. То есть классы, полученные энумерацией, имеют методы, вызов которых приведет к выполнению на целевой машине неких, подлежащих ему действий. Не думайте, что я описался, назвав локальный компьютер целевым. WMI позволяет проводить все эти операции с любым хостом Windows в сети, лишь бы имелись административные права на подключение к целевой машине.
Итак, пора написать программку, которая будет нашим телескопом в локальной сети.
Show must go on…
Ради интереса, предлагаю небольшой холливар. Да, да… Вот такой вот я нехороший провокатор. Предлагаю написать программу на двух языках – Делфи и Си. В качестве компиляторов я взял Delphi 6 (на мой взгляд, самая лучшая версия Делфи) и Visual Studio 2010. Не потому что VS2008 или VS6 хуже, а просто у меня нет их под рукой.
Для начала давайте определимся со стратегией. Чтобы получить набор данных от WMI нам нужно следующее:
- запустить СОМ сервер, провайдер WMI. А точнее WbemLocator – это имя провайдера;
- приконнектить его к целевой машине;
- заставить его выполнить запрос;
- пройтись энумератором по коллекции записей;
- пройтись энумератором по коллекции полей каждой записи.
Мы, кстати, реализуем механизмы получения значения поля, как по его названию, так и по его номеру. Для этого предлагаю выделить два класса. Первый назовем TWMI. Он будет главным. Его задача принять запрос, подключится к компьютеру, выполнить запрос, и, перебрав его записи, создать список объектов типа TWMIRecord. Этот второй класс будет отвечать за получение данных из полей переданной ему записи (записи передаются в виде объекта) либо по имени, либо по номеру поля. Опять таки и тут без энумерации не обойтись.
Ладушки. Начнем. Сначала опишем главный класс. Листинг 1 и 2 показывает, как он может выглядеть в Делфи и Си:
Листинг 1 (Делфи)
private
// Список объектов-записей
FRecords:TObjectList;
FSQL: String;
FRoot: String;
FHost: String;
FSQL2: String;
FLogin: String;
FPassword: String;
procedure SetHost(const Value: String);
procedure SetRoot(const Value: String);
function GetItem(i: Variant): TWMIRecord;
procedure SetSQL2(const Value: String);
procedure SetLogin(const Value: String);
procedure SetPassword(const Value: String);
Public
//**************************************
// Функция для циклов. Выдает номер последней записи
Function HighObject:Integer;
// Функция поиска записи по имени поля
Function Find(AFieldName,AValue:String):TWMIRecord;
//**************************************
// Свойство, получающее по номеру запись из набора
Property Item[i:Variant]:TWMIRecord read GetItem; default;
Constructor Create(AOwner:TComponent);
Destructor Free;
published
// Свойства Логина и пароля. Чтоб подключится к компьютеру и
// получить информацию нужны привилегии администратора
Property Login:String read FLogin write SetLogin;
Property Password:String read FPassword write SetPassword;
// Имя машины к которой подключаемся
Property Host:String read FHost write SetHost;
// Путь к таблице с данными
Property Root:String read FRoot write SetRoot;
// Свойство, принимающее строку запроса
// и инициализирующее его выполнение
Property SQL:String read FSQL2 write SetSQL2;
end;
//******************************************Syhi-подсветка кода
Листинг 2 (С++)
{
private:
// Обьект-провайдер, через который можно будет приконнектится
// к компьютеру.
IWbemLocator *loc;
// обьект, которому будем скармливать запрос, и получать
// записи
IWbemServices *serv;
// поле, хранящее наш запрос
string FQuery;
// энумератор записей
IEnumWbemClassObject* enum_Record;
// список, который будет хранить объекты-записи
list<TWMIRecord> RecList;
// функция, активирующая запуск провайдера
bool RunLocatorInstance();
// функция, активирующая подключение к хосту
bool ConnectToWBEM();
// процедура, создающая новыйй обьект-запись
// в ходе энумерации
void CreaRecord(IWbemClassObject *O);
public:
TWMI(void);
~TWMI(void);
// Функция, открывающая набор данных
// по переданному ей запросу
bool SetQuery(string wql);
// Функция, получающая запись по ее номеру
TWMIRecord* Item(int i);
};Syhi-подсветка кода
На всякий случай хочу сказать, что для Сишного кода нужны инструкции:
Листинг 3
#pragma comment(lib, "wbemuuid.lib")
#include <comdef.h>
#include <WbemIdl.h>
#include "TWMIRecord.h"
#include <string>
#include <list>
using namespace std;Syhi-подсветка кода
Здесь единственный хедер, который стоит описать – TWMIRecord.h. Он описывает наш класс, отвечающий за получение и хранение полей записи, переданной ему. Все остальное – стандартный набор. Я не буду их описывать, ибо их описание вполне можно найти на MSDN или в хелпе.
Теперь можно описать конструкторы и деструкторы главных классов. На Делфи:
begin
inherited;
FRecords:=TObjectList.Create;
FHost:=‘.’;
FRoot:=‘root\cimv2’;
end;
destructor TWMI.Free;
begin
FRecords.Free;FRecords:=nil;
end;Syhi-подсветка кода
На Си:
{
loc=NULL; serv=NULL;
CoInitialize(0);
}
TWMI::~TWMI(void)
{
CoUninitialize();
RecList.clear();
Здесь мало интересного. Инициализируются главные свойства, и в случае с Си инициализируется (в конструкторе) и сбрасывается (в деструкторе) СОМ библиотека. В случае с Делфи инициализация будет не здесь, а в коде обработки запроса.
Для Делфи:
На всякий случай, чтоб не было недомолвок, скажу, что обработчики свойств Login, Password, Host, Root стандартные для любого проекта, я не буду приводить их реализацию, ибо там всего лишь внутреннему полю класса, отвечающему за значение этих свойств, описывается методика присвоения нового значения. Если что станьте курсором на эти свойства и нажмите CTRL+SHIFT+C – будут созданы стандартные тела обработчиков – именно их я и имею в виду.
Самое главное для этого класса – метод обработки запроса. Именно здесь сконцентрирована вся магия работы с WMI. Давайте посмотрим, как она выглядит (на Делфи):
var
// Переменная провайдер. подключается к хосту,
// Через нее будем получать объект для обработки
// запросов
objSWbemLocator,
// Переменная объекта обработки запросов
// Ей будем скармливать запрос и ее же
// энумератором будем получать записи
objWMIService,
// Сервисные переменные. Нужны для
// получения энумераторов
Records,o1,o2
:OleVariant;
i:Cardinal;
// Интерфейс, который будет являться посредником
// между провайдером и объектом запросов
id:IDispatch;
// Энумератор для прохода по записям
Enum:IEnumVariant;
// Переменная класса, который будет обрабатывать
// переданную ему запись
r:TWMIRecord;
begin
FSQL2 := Value;
// инициализируем СОМ модель
CoInitialize(0);
// Получаем объект провайдера WMI, Локатор, если говорить
// по микрософтовски
objSWbemLocator:=CreateOleObject(‘WbemScripting.SWbemLocator’);
// Если он успешно получен, то можно далее с ним работать
if not VarIsClear(objSWbemLocator) then begin
// Присоединяемся к хосту, получив интерфейс для
// отработки запросов
objWMIService:=objSWbemLocator.ConnectServer(Host,Root,FLogin,FPassword,»,»,0,id);
// Если присоединение прошло успешно то можно
// скармливать запрос
if not VarIsClear(objWMIService) then begin
// Скормим запрос WQL нашему провайдеру
Records:=objWMIService.ExecQuery(FSQL2,‘WQL’,0,id);
// Он вернет объект записей. Ну если конечно вернет
if not VarIsClear(Records) then begin
//**************************************
// В случае когда все удачно, инициализируем
// энумератор для прохода по записям
Enum:=IEnumVariant(IUnknown(Records._NewEnum)); //Список Записей
// Приготовим список для наполнения объектами,
// обрабатывающими переданные им записи
FRecords.Clear;
// и пройдемся энумератором, пока он
// не достигнет конца коллекции записей
// или точнее пока очередная запись выбрана успешно
while (Enum.Next(1, o1, i) = S_OK) do begin
// Создадим объект — запись
r:=TWMIRecord.Create;
// внеся его в список
FRecords.Add(r);
// И заставим его пройтись по полям,
// переданной ему записи
r.Enum(o1);
end;
//**************************************
// после чего приберем мусор. Набор мы уже получили
// так что можно отключаться
Records:=Unassigned;
end;
objWMIService:=Unassigned;
end;
objSWbemLocator:=Unassigned;
end;
// и освободить ресурсы СОМ машины.
CoUninitialize;
end;Syhi-подсветка кода
Добавлю, что у класса TWMIRecord предусмотрен метод Enum, которому передается объект записи. Он инициализирует проход по полям, получая из них значения. Теперь в С++:
Листинг 7
// Переменная для результатов СОМ механизмов
HRESULT hres;
// результат выполнения запроса.
// он будет подаваться на выход метода, дабы
// программист знал, отработал ли метод успешно
// или не отработал
bool res=false;
// Если Локатор запустился нормально
if(RunLocatorInstance()){
// И присоединился к хосту
if(ConnectToWBEM()){
// Можно скармливать провайдеру
// запрос, не забыв преобразовать типы 🙂
hres=serv->ExecQuery(bstr_t("WQL"),
bstr_t(wql.c_str()),
WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
NULL,
&enum_Record);
// Если запрос скормлен, и провайдер отработал его правильно
// можно идти далее. Предидущей функцией был
// получен энумератор записей, который позволит
// сделать проход по ним
if(!FAILED(hres)){
// Обьявим переменную — запись. В нее на итерации
// будет подаваться очередная запись
IWbemClassObject *Obj;
// и переменную, в которую будет подаваться сколько // записей
// выбрано. На итерациях это значение будет равно 1,
// ведь мы будет проходитьпо каждой записи 🙂
ULONG uReturn = 0;
hres=S_OK;
// приготовим список, хранящий объекты записей
RecList.clear();
// и прокатимся по энумератору
while(enum_Record){
// получим очередную запись
hres=enum_Record->Next(
//указав что мы будем ждать до последнего, пока на сервере выбирается запись
WBEM_INFINITE,
// проходя по одной записи за итерацию,
1,
// передавая ее в переменную вышеописанную нами для этого
&Obj,
// и получая ответ "сколько записей пройдено", короче 1
&uReturn
);
// Если же всетки мы достигли конца набора, вернется 0 пройденных
// записей. Придется прервать цикл
if( uReturn==0) break;
// А пока записи прибывают будем составлять на них
// опись, протокол, сдал-принял… отпечатки пальцев 😀
CreaRecord(Obj);
}
// по прошествии цикла будем считать что функция успешно отработала
// соответственно вернем позитивный ответ
res=true;
}
// и освободим нашего провайдера и его сервис
serv->Release();
}
loc->Release();
}
return res;
};Syhi-подсветка кода
Здесь применены методы, описанные выше:
HRESULT hres;
bool rt=false;
hres=CoCreateInstance(CLSID_WbemLocator,0,CLSCTX_INPROC_SERVER,
IID_IWbemLocator,(LPVOID *)&loc);
if(!FAILED(hres)){
hres = CoInitializeSecurity(
NULL,
-1, // COM authentication
NULL, // Authentication services
NULL, // Reserved
RPC_C_AUTHN_LEVEL_DEFAULT, // Default authentication
RPC_C_IMP_LEVEL_IMPERSONATE, // Default Impersonation
NULL, // Authentication info
EOAC_NONE, // Additional capabilities
NULL // Reserved
);
rt=(!FAILED(hres))?true:false;
}
return rt;
}
bool TWMI::ConnectToWBEM(){
HRESULT hres;
BSTR bUser=NULL,bPassword=NULL;
BSTR bHost=L"ROOT\\CIMV2";
if(Host!=""){bHost=L"\\\\"+_bstr_t(Host.c_str())+L"\\ROOT\\CIMV2";}
hres=loc->ConnectServer(
bHost, // Хост с веткой WMI
// User name. NULL = current user
(Login!="")?_bstr_t(Login.c_str()):bUser,
// User password. NULL = current (Password!="")?_bstr_t(Password.c_str()):bPassword,
0, // Locale. NULL indicates current
NULL, // Security flags.
0, // Authority (e.g. Kerberos)
0, // Context object
&serv // pointer to IWbemServices proxy
);
return (!FAILED(hres))?true:false;
};Syhi-подсветка кода
В принципе, здесь пояснять особо нечего. Создается инстанция провайдера, говоря по-русски, запускается для нашей программы механизм WMI (аналог CreateOleObject(‘WbemScripting.SWbemLocator’); примененной мной в Делфи варианте) и происходит подключение к хосту, учитывая логин и пароль пользователя.
Если логин и пароль отсутствуют, берется аккаунт текущего сеанса (Если уж по правде то текущий контекст безопасности, т.е. с чьими правами запущена программа. Будем считать, что запуск программы будет от текущего сеанса. Мы ведь рассчитываем, что это будет запускать администратор всея сети). Если отсутствует указание хоста – подключение идет к нашей машинке.
Почему я выделил в Сишном классе эти функции подключения отдельно? Захотелось так. Мне показалось, что слишком много в Си писанины, и я не хотел загромождать метод обработки запроса лишним кодом, это бы выглядело некрасиво. Казалось бы сложным. На Делфи эти функции как-то срослись в две строчки, к сожалению, в Си мне не удалось так же аккуратно написать их, может потому, что я плохо знаю Си, или потому что в Делфи действительно удобнее описывать работу с СОМ интерфейсами… Да и работа со строками в Делфи попрозрачнее будет… Впрочем, это не важно. Ну и сюда же следует приписать реализацию процедуры, создания класса для обработки записи:
// создаем новый объект, скормив ему
// переданную строку
TWMIRecord *r=new TWMIRecord(O);
// Помещаем его в список строк
RecList.push_back(*r);
};Syhi-подсветка кода
Энумерация по полям записи будет инициирована конструктором. Опять таки, в Си я описал создание класса в отдельную процедуру не по каким-то особым причинам, а потому что просто так захотелось. Запросто эти две строчки можно поместить в энумерацию SetQuery.
Так, набор получили. Прокатились по нему. Теперь нужен механизм, проходя по списку объектов TWMIRecord. Надо же как-то получать выбранные данные. Посмотрим, как это можно сделать (на Делфи):
var ii:integer;
begin Result:=nil;
if VarIsOrdinal(i) then
// Если переданный номер записи не
// вылезает за рамки списка
// получим ее
if (i>=0)and(i<FRecords.Count) then
Result:=TWMIRecord(FRecords[i]);
end;Syhi-подсветка кода
Эта функция повешена на свойство:
Хорошо зная Делфи, могу заверить, что возможность объявлять свойство для класса по умолчанию очень полезна. Достаточно в выражениях указать имя переменной объекта, если какое-то из его свойств установлено по умолчанию, оно вызовется, так как будто программист руками его вызвал в коде.
Например, многим известен Дельфийский Listbox. У него есть свойство Items[номер строки]. Вызывают его так ListBox.Items[такой-то] – Это дает строку по указанному номеру. Но далеко не все знают, что при написании такого на самом деле Делфи воспринимает это указание как ListBox.Items.Strings[такая-то], потому что Strings описана как свойство по умолчанию для поля Items, да еще и с указанием индексации. Согласитесь удобнее не писать Items.Strings раз среда позволяет это. Код становится короче и красивее. Кстати некоторые нерадивые авторы методичек на этом деле спекулируют. В одних методичках пишут длинную форму обращения, в других краткую. Учеников это часто путает, появляется вопросы «В каких случаях писать длинную форму инструкции в каких короткую». Ответ то на самом деле прост – эти две формы равнозначны. Кто как хочет, пусть так и пишет. Линейкой по рукам нужно проехаться этим борзописцам-преподавателям. Сразу видно они сами мало понимают в программировании. Это большая беда нашей системы обучения, но, увы… Законы Подлости диктуют свою игру.
Продолжение следует…
The Чтиво
- Ресурс вики http://ru.wikipedia.org/wiki/WMI
- MSDN http://social.msdn.microsoft.com/Search/ru-RU?query=WMI&ac=8
- MSDN. Win32 Classes http://msdn.microsoftcom/en-us/library/aa394084%28v=VS.85%29.aspx
- Марк Русинович. Внутри Windows Management Interface. – Magazine/RE, 05, 2000
Статья из восьмого выпуска журнала «ПРОграммист».
Обсудить на форуме — WMI. Wладение Mагической Iнформацией. часть 1
21st
Фев
Panic button — WinAPI графика
Здравствуйте, уважаемые читатели. Прочитав эту статью, Вы получите навыки создания «чистых» WinAPI приложений и управления графикой в среде разработки Lazarus.
21st
Эксплойты. Анализ состава преступления
С каждым годом происходит увеличение количества объектов и источников информации. Для организаций и отдельных лиц становится необходимостью обеспечение трех базовых принципов информационной безопасности: целостности, доступности, конфиденциальности. Обеспечение правовой защиты компьютерной информации выходит на первый план в деятельности любой организации. Поэтому компьютерные преступления, любое противоправное действие, при котором компьютер выступает либо как объект, против которого совершается преступление, либо как инструмент, используемый для совершения преступных действий, могут привести к серьезным убыткам и к другим негативным последствиям. (читать всё…)
21st
Почему сыр в мышеловке бесплатный?
По данным ассоциации производителей программного обеспечения (BSA) в 2009 году 67% программного обеспечения в России было установлено незаконным путем. И 43 % исходят из 111 стран мира. Потери мировой индустрии от распространения пиратского софта оцениваются в 51.4 млрд долларов. Рынок бесплатного программного обеспечения (само словосочетание уже является парадоксом) в Российской Федерации пока что находится в зачаточном состоянии (по данным 2009 года), однако быстро развивается. Так почему же во многих странах складывается ситуация когда пользователь не желает платить за программы? Сегодня мы с вами поговорим о причинах использования бесплатного и пиратского программного обеспечения…
21st
Работа с MySQL в С++ с использованием библиотеки mysql++
Под впечатлением от предыдущей статьи форумчанина Psycho-coder, я решил написать свой небольшой мануал по работе с СУБД MySQL, используя библиотеку mysql++. Данная библиотека является кроссплатформенным решением, написанным на С++, и предоставляет богатый набор классов, позволяяя создавать эффективные приложения.
21st
Компилятор домашнего приготовления. Часть 2
Ветер… У вас тоже на улице ветерок? Возможно, он принесет дождь, по крайней мере, разгонит эту изнурительную жару. Но пока этого не случится, людям стоит спрятаться от палящих лучей солнца в помещения с кондиционером и холодильником, в котором охлаждается квасок или живое пиво. Ну, а пока это счастье свежеет от фреонного холода, есть время продолжить разработку своего компилятора…
21st
Layered в windows. Использование слоев
Многие профессионалы и новички в программировании под Windows задаются вопросом, как создавать окна необычной формы с использованием различных эффектов, таких как тени, к примеру. Такие окна часто можно встретить в виде виджетов/дополнений на рабочем столе Windows 7/Vista или же заставки при запуске приложений – к примеру, Photoshop (все мы помним то перышко, выходящее за границу прямоугольника). Также имеет смысл создавать подобные окна с полноценными элементами управления: данная технология позволяет форматировать вывод окна попиксельно, то есть можно настроить прозрачность и цвет любого пикселя окна вашего приложения – разве не здорово?
20th
Фев
Беспроводная сеть масштаба микрорайона. Часть 2
В первой части нашего материала уже было сказано, что в качестве основной операционной системы для серверов сети была выбрана платформа Windows. Почему не Linux или, например, FreeBSD? Закономерный вопрос, причин такого выбора несколько…
20th
Маленькие помощники программиста. Часть 2
Часто самой большой проблемой программиста становится сохранение проекта в целости и сохранности. Как не допустить уничтожения проекта в процессе работы и создать сайт для любого браузера – об этом вы узнаете в статье «Маленькие помощники программиста».
Алексей Шишкин
by AlexCones http://flsoft.ru
В этой части нашего «путешествия» по обстановке программиста будущего я бы хотел рассказать о таком важном слове, как бекап*, и о моем «уголке веб-девелопера».
(англ) BackUp – резервная копия
Итак, начнем с бекапов, как самого важного в жизни программистов. Да, вы не ослышались, это важнее, чем знать в совершенстве какой-либо язык программирования, ибо, если дать двум программистам, новичку и профессионалу, в руки среду разработки и дать инструмент бекапа только в руки новичка, при повреждении данных именно он выйдет победителем. Итак, начнем. Для начала нам понадобится OpenSource** программа 7zip и Delphi-образная (к примеру, Lazarus) среда разработки.
- Для начала создадим на жестком диске папку, куда будем складывать наши бекапы (см. рисунок 1):
Рис. 1. Создаем директорию*** для копий
- Определившись с папкой, загружаем утилиту 7z http://www.7-zip.org
- Устанавливаем ее в папку по вашему усмот- рению. Отныне я буду называть ее %7ZFolder%, а папку, которую мы создали для хранения бекапов, – %CopyFolder%.
- Теперь зайдем в %7ZFolder% и создадим там *.txt файл следующего содержания:
7z.exe a %DATE%.7z D:\Finder
Move %DATE%.7z D:\Copy\Finder\
Cd D:\Copy\Finder
Renamer %DATE%.7zГде, в данном случае: D:\Finder – папка проекта, который мы будем бекапить, D:\Copy\Finder – %CopyFolder%.
- Переименовываем этот файл в *.bat
- Но что же значит загадочный Renamer? Его созданием мы сейчас и займемся.
- Открываем среду разработки и, создав Console Application, пишем:
Program Renamer;
Uses Windows, SysUtils;
Var
Name1, Name2,S : String;
I : Integer;
Begin
Name1 := ParamStr(1);
S := Name1;
Name2 := »;
While Pos(‘\’,S)<>0 Do Begin
Name2 := Name2 + S[1];
Delete(S,1,1);
End;
For I := 1 to Length(S) — 2 Do
Name2 := Name2 + S[I];
Name2 := Name2 + TimeToStr(Time) + S[Length(S)-2] +
S[Length(S)-1] + S[Length(S)];
For I := 1 To Length(Name2) Do
If Name2[I] = ‘:’ Then Name2[I] := ‘.’;
RenameFile(Name1, Name2)
end.Думаю, не надо пояснять, что делает этот код. Компилируем его и отправляем Renamer.exe в %CopeFolder%.
* Комментарий автора.
С открытым исходным кодом, распространяющаяся по лицензии
GNU GPL.* Комментарий автора.
Имена для папок желательно выбирать короткими и без кириллицы: будет меньше проблем с *.bat файлом. - Вернемся к нашей основной мысли: как же нам запустить все это вместе? Создадим новый проект (как и прошлый, он будет Console Application). Обратите внимание, что в обоих случаях строк {$ Console Application} НЕ БУДЕТ. Сделано это для того, чтобы консоль не мозолила нам глаза:
program Watcher;
uses ShellAPI, Windows;
Var
Look : Boolean = FALSE;
Gotcha : Boolean = FALSE;
Hand : Integer;
Begin
While True Do Begin
(* Search for Lazarus *)
Hand := 0;
Hand := FindWindow(‘Window’, ‘Lazarus IDE v0.9.28.2 бета — Finder.lpi’);
Gotcha := (Hand <> 0);
If (Look) And Not(Gotcha) Then Begin
ShellExecute(0,‘Open’,‘D:\Programs\7-Zip\Arc.bat’, Nil, Nil, SW_HIDE);
Look := FALSE;
End
Else If Not(Look) And (Gotcha) Then Look := TRUE;
Sleep(10 * 1000);
End
End.Разумеется, следует заменить директории в коде на свои. Обратите внимание, что я использовал для поиска заголовок и класс окна лазаруса, так как свой проект я создаю в нем. Замените их на класс и заголовок окна своей среды разработки.
- Итак, мы почти пришли, осталось поместить ярлык на эту программу в папку ПУСК ? Все программы ? Автозагрузка, и дело сделано. Теперь достаточно перезагрузить компьютер или вручную запустить Watcher.exe, и система начнет работать. Как же она работает? Очень просто:
Watcher раз в 10 секунд ищет заголовок и класс среды разработки с открытым проектом для бекапа:
- если она найдена, флаг Gotcha будет >0;
- продолжаем следить за процессом, и если окно не будет найдено, действуем;
- запускается bat файл Arc.bat;
- он бекапит папку с проектом и дает ей имя сегодняшней даты;
- затем он отправляет его Renamer`у, который переименовывает его согласно дате и времени.
Результат наших действий (см. рисунок 2):
Рис. 2. Часть бекапов в папке после часа работы (чтобы уменьшить их количество, измените число после Sleep в большую сторону)
Итак, мы научились бекапить свои проекты, и теперь перебои электричества и повреждения жесткого диска нам не страшны. Только не забывайте хотя бы изредка сбрасывать свои бекапы на съемный диск или CD/DVD.
Перейдем ко второй части нашего рассказа
ведь я еще обещал рассказать про свой «уголок» веб-девелопера… Понадобилось мне как-то сваять в блокноте сайт для одного из своих проектов. Разумеется, я обладал некоторыми основами сайтостроения, но, разумеется, я (как и большинство веб-девелоперов) не знал заранее, как сайт будет выглядеть во всех браузерах.
И сейчас расскажу, как я избавился от этой проблемы.
Для создания этого древнего заклинания нам потребуется:
- артефакт «Флеш карта» размером от 1 Гб; доступ в Интернет на 15-20 минут;
- Delphi-образная среда разработки;
- 10 минут приготовлений.
Итак, переходим на сайт http://torwald.ru/72 и выкачиваем ВСЕ браузеры, которые видим. По очереди запускаем инсталляторы и устанавливаем их на флешку. Некоторые из браузеров даже не потребуют установки – их просто копируем.
Далее пробуем воссоздать такую схему (см.рисунок 3):
Рис. 3. Папки с программами
Теперь, когда мы все это установили, объединим все это одним заклинани- ем, чтобы не утруждать себя чтением древних свитков каждые пять минут:
Uses ShellAPI, Windows;
Var
FileName : String;
CurDir : String;
F : TextFile;
Begin
FileName := ParamStr(1);
CurDir := ‘H:\WEB’;
AssignFile(F, CurDir + ‘\Run.bat’);
Rewrite(F);
WriteLn(F, CurDir + ‘\InternetExplorer-6\ie6.exe ‘ +FileName);
WriteLn(F, CurDir + ‘\InternetExplorer-7\ie7.exe ‘ +FileName);
WriteLn(F, CurDir + ‘\InternetExplorer-8\ie8.exe ‘ +FileName);
WriteLn(F, CurDir + ‘\Chrome-4.0\Chrome.exe ‘ + FileName);
WriteLn(F, CurDir + ‘\Safari-3.2\safari.exe ‘ + FileName);
WriteLn(F, CurDir + ‘\Firefox-2\firefox2.exe ‘ + FileName);
WriteLn(F, CurDir + ‘\Firefox-3\firefox3.exe ‘ + FileName);
WriteLn(F, ‘pause’);
CloseFile(F);
ShellExecute(HWND(0), ‘Open’, PChar(‘Run.bat’), »,
PChar(‘H:\WEB’), SW_SHOWNORMAL);
End.
Вместо H:\ следует подставить букву диска своей флешкарты. Поместим эту программу в папку WEB на флешкарте и создадим ярлык на ней, который поместим в директорию C:\Documents and settings\%Username%\SendTo. Не забудьте переименовать ярлык в WEB test.lnk
Все, заклинание готово! Запускать его будем так: клик правой кнопкой на HTML\HTM документ, который необходимо проверить, и выбираем ОТПРАВИТЬ – WEB test. Документ откроется по очереди в разных браузерах, и вы сможете увидеть, как он выглядит у пользователей.
Заключение
При разработке использовалась среда разработки – Lazarus 0.9.28 и блокнот. Дело шло, как ни странно, под «окошками».
Итак, мы научились сохранять свои проекты в условиях глобальных катаклизмов и создавать сайты для каждого браузера. Это лишь часть знаний, которые помогут вам в жизни, но надеюсь, что когда-нибудь они вам пригодятся.
Удачи!
Ссылки
- 7zip архиватор http://www.7-zip.org
- Portable версии браузеров http://torwald.ru/72
- Скачать среду разработки Lazarus http://www.lazarus.freepascal.org
Статья из седьмого выпуска журнала «ПРОграммист».
Обсудить на форуме — Маленькие помощники программиста. Часть 2
Облако меток
css реестр ассемблер timer SaveToFile ShellExecute программы массив советы word MySQL SQL ListView pos random компоненты дата LoadFromFile form база данных сеть html php RichEdit indy строки Win Api tstringlist Image мысли макросы Edit ListBox office C/C++ memo графика StringGrid canvas поиск файл Pascal форма Файлы интернет Microsoft Office Excel excel winapi журнал ПРОграммист DelphiКупить рекламу на сайте за 1000 руб
пишите сюда - alarforum@yandex.ru
Да и по любым другим вопросам пишите на почту
пеллетные котлы
Пеллетный котел Emtas
Наши форумы по программированию:
- Форум Web программирование (веб)
- Delphi форумы
- Форумы C (Си)
- Форум .NET Frameworks (точка нет фреймворки)
- Форум Java (джава)
- Форум низкоуровневое программирование
- Форум VBA (вба)
- Форум OpenGL
- Форум DirectX
- Форум CAD проектирование
- Форум по операционным системам
- Форум Software (Софт)
- Форум Hardware (Компьютерное железо)