воскресенье, 31 октября 2021 г.

Создаем GTK список (виджет)

 Приведу без комментариев код. 

// gtktable.c
#include <stdio.h>
#include <gtk-3.0/gtk/gtk.h>

GtkWidget* main_window;
GtkWidget* box1;
GtkWidget* scroll_window;

GtkWidget* list_widget;
GtkTreeStore* store_list;
enum{COL1, COL2, COL3, NCOL};
GtkTreeViewColumn *column;
GtkCellRenderer *renderer;
GtkTreeSelection* selection;
GtkTreeIter iter1;

gboolean
selection_func (GtkTreeSelection *selection,
                     GtkTreeModel     *model,
                     GtkTreePath      *path,
                     gboolean          path_currently_selected,
                     gpointer          userdata)
{
    g_print ("selection_func is called.\n");
    return TRUE;
}

void
create_gtk_table(){
    store_list = gtk_tree_store_new(NCOL, G_TYPE_INT, G_TYPE_STRING, G_TYPE_STRING);
    list_widget = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store_list));
    selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(list_widget));
    gtk_tree_selection_set_select_function(selection, selection_func, NULL, NULL);

    gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(list_widget), TRUE);
    gtk_tree_view_set_grid_lines(GTK_TREE_VIEW(list_widget),GTK_TREE_VIEW_GRID_LINES_BOTH);
    gtk_container_add(GTK_CONTAINER(scroll_window), list_widget);

    renderer = gtk_cell_renderer_text_new();
    g_object_set(G_OBJECT(renderer), "background", "grey", NULL);
    g_object_set(G_OBJECT(renderer), "foreground", "black", NULL);

    column = gtk_tree_view_column_new_with_attributes("Title1",renderer, "text", COL1, NULL);
    gtk_tree_view_column_set_clickable(column, TRUE);
    gtk_tree_view_append_column(GTK_TREE_VIEW(list_widget), column);

    column = gtk_tree_view_column_new_with_attributes("Title2",renderer, "text", COL2, NULL);
    gtk_tree_view_append_column(GTK_TREE_VIEW(list_widget), column);

    column = gtk_tree_view_column_new_with_attributes("Title3",renderer, "text", COL3, NULL);
    gtk_tree_view_append_column(GTK_TREE_VIEW(list_widget), column);

    g_object_unref(G_OBJECT(store_list));

    for(gint i = 0 ; i < 100; i++){
      gtk_tree_store_append(store_list, &iter1, NULL);
      gtk_tree_store_set(store_list, &iter1, COL1, i+1, COL2, "string ?", COL3, "the string!", -1);
    }
}

void
activate (GtkApplication *app,
          gpointer        user_data)
{
    main_window = gtk_application_window_new (app);
    gtk_window_set_title (GTK_WINDOW (main_window), "The Table in The Scrolling");
    gtk_window_set_default_size (GTK_WINDOW (main_window), 400, 500);
    gtk_container_set_border_width (GTK_CONTAINER (main_window), 20);

    box1 = gtk_button_box_new (GTK_ORIENTATION_VERTICAL);
    gtk_box_set_homogeneous (GTK_BOX(box1), FALSE);
    gtk_container_add (GTK_CONTAINER (main_window), box1);

    scroll_window = gtk_scrolled_window_new(NULL, NULL);
    gtk_widget_set_size_request(scroll_window, 350, 450);
    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll_window), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
    gtk_box_pack_start(GTK_BOX (box1), scroll_window, FALSE, FALSE, 10);

    create_gtk_table();

    gtk_widget_show_all (main_window);
}

int main(int argc, char **argv) {
    GtkApplication *app;
    int status;
    app = gtk_application_new ("org.gtk.the_sin", G_APPLICATION_FLAGS_NONE);
    g_signal_connect (app, "activate", G_CALLBACK (activate), NULL);
    status = g_application_run (G_APPLICATION (app), argc, argv);
    g_object_unref (app);
    return status;
}

Код, который ответственнен за создание списка находится в функции  create_gtk_table. После выполнения эта программа выводит на дисплей вот такое:

Если для компиляции ипользовать CMake, то необходимо создать файл CMakeLists.txt следующего содержания:

cmake_minimum_required (VERSION 3.18)

project (gtktable)
find_package (PkgConfig REQUIRED)
pkg_check_modules (GTK3 REQUIRED gtk+-3.0)
include_directories (${GTK3_INCLUDE_DIRS})
link_directories (${GTK3_LIBRARY_DIRS})
add_executable(gtktable gtktable.c)
add_definitions (${GTK3_CFLAGS_OTHER})
target_link_libraries (gtktable ${GTK3_LIBRARIES})

Компилировалось и выполнялось на Debian Bullseye. Собственно, это все, что хотел сказать.

четверг, 14 октября 2021 г.

Простейшее испльзования систем CMake и Meson на примере gtk+ приложения

 Напишем простое gtk приложение, которое считает sin от вводимой величины. 

//   calc_sin.c
#include <gtk/gtk.h>
#include <math.h>

GtkWidget* main_window;
GtkWidget* box1;

GtkWidget* edit1;
GtkWidget* button1;
GtkWidget* label1;
GtkWidget* button_exit;

void
calc_sin(){
    double result = sin(atof(gtk_entry_get_text(GTK_ENTRY(edit1))));
    gchar *display = g_strdup_printf("%f", result);   
    gtk_label_set_text (GTK_LABEL(label1), display);
    g_free(display); 
}

void
activate (GtkApplication *app,
          gpointer        user_data)
{
      main_window = gtk_application_window_new (app);
      gtk_window_set_title (GTK_WINDOW (main_window), "The sin");
      gtk_window_set_default_size (GTK_WINDOW (main_window), 200, 400);
      gtk_container_set_border_width (GTK_CONTAINER (main_window), 20);

      box1 = gtk_button_box_new (GTK_ORIENTATION_VERTICAL);
      gtk_box_set_homogeneous (GTK_BOX(box1), FALSE);
      gtk_container_add (GTK_CONTAINER (main_window), box1);

      edit1=gtk_entry_new();
      gtk_box_pack_start(GTK_BOX (box1), edit1, FALSE, FALSE, 10);

      button1 = gtk_button_new_with_label("Calculate!");
      g_signal_connect (button1, "clicked", G_CALLBACK (calc_sin), NULL);
      gtk_box_pack_start(GTK_BOX (box1), button1, FALSE, FALSE, 10);

      label1 = gtk_label_new("result");
      gtk_box_pack_start(GTK_BOX (box1), label1, FALSE, FALSE, 10);

      button_exit = gtk_button_new_with_label ("Exit");
      gtk_box_pack_start(GTK_BOX (box1), button_exit, FALSE, FALSE, 10);
      g_signal_connect_swapped (button_exit, "clicked", G_CALLBACK
(gtk_widget_destroy), main_window);

      gtk_widget_show_all (main_window);
}

int
main (int    argc,
      char **argv)
{
    GtkApplication *app;
    int status;
      app = gtk_application_new ("org.gtk.the_sin", G_APPLICATION_FLAGS_NONE);
      g_signal_connect (app, "activate", G_CALLBACK (activate), NULL);
      status = g_application_run (G_APPLICATION (app), argc, argv);
      g_object_unref (app);
      return status;
}

 А теперь откомпилируем-отлинкуем этот код разными методами и выполним его. Предполагается, что cmake и meson уже установлены.

Сначала сделаем это "традиционно":

#gcc calc_sin.c `pkg-config  --cflags --libs  gtk+-3.0` -lm -o calc_sin_bin

 После выполнения полученного бинарника мы должны увидеть следующее:

В едитбокс вводим величину в радианах, нажимаем на кнопочку "Calculate!" и вместо слова "result" увидим величину синуса введенного значения.

Теперь соберем нашу программу с помощью CMake.
Создадим файл CMakeLists.txt следующего содержания:

cmake_minimum_required (VERSION 2.6)

project (calc_sin_prj)
find_package (PkgConfig REQUIRED)
pkg_check_modules (GTK3 REQUIRED gtk+-3.0)
include_directories (${GTK3_INCLUDE_DIRS})
link_directories (${GTK3_LIBRARY_DIRS})
add_executable(calc_sin_bin calc_sin.c)
add_definitions (${GTK3_CFLAGS_OTHER})
target_link_libraries (calc_sin_bin ${GTK3_LIBRARIES} m)

Этот файл в данном случае должен быть в директории с нашим исходником. Далее делаем такую последовательность команд:

#mkdir cmakebuilddir
#cd cmakebuilddir
#cmake ..
#cmake --build .

Теперь соберем нашу программу с помощью meson.
Создадим файл meson.build следующего содержания:

project('calc_sin_meson', 'c')
cc = meson.get_compiler('c')
m_dep = cc.find_library('m', required:false)
deps = [dependency('gtk+-3.0'), m_dep]
executable('calc_sin_bin', 'calc_sin.c', dependencies : deps)

 Делаем такую последовательность команд:

#meson setup mesonbuilddir
#cd mesonbuilddir
#meson compile

Во всех трех случаях получаем бинарник  calc_sin_bin с одинаковою функциональностью. Обратите внимание, что этот бинарник создается в случае meson в директории mesonbuilddir , а в случае CMake в директории  cmakebuilddir. Получается что дерево исходников находится отдельно, а все рабочие файлы каждой системной сборки (мезон и симэйк) находятся только в своей поддиректории соответственно. Аккуратно и логично.

суббота, 9 октября 2021 г.

Навигация по документу в vim

    Заезженная тема, но достойная отдельного рассмотрения. Рассмотрим команды перемещения по открытому документу в редакторе vim в нормальном режиме (есть еще режим редактирования, визуальный режим и режим ввода команд после двоеточия). Команды интуитивно сгруппированы в отдельные таблицы и параграфы.
 

аналогия "обычных" клавиш "стрелка вверх" и "стрелка вниз" курсор относительно текста не двигается, текст смещается на одну строку курсор относительно текста не двигается, текст смещается на полэкрана текст смещается на полный экран
вверх Ctrl-p
Ctrl-y Ctrl-u
Ctrl-b
вниз Ctrl-n Ctrl-e Ctrl-d
Ctrl-f

 Ctrl-y и Ctrl-e полезно запомнить, так как эти комбинации имеют полезную функциональность в режиме редактирования: копируют букву в текущей позиции в верхнюю или нижнюю строку соответственно.

текст не перемещаетсяотносительно окна, двигается только курсор
 текст перемещается относительно окна, курсор привязан к позиции в тексте
влево h

вниз j, на дно экрана L
вниз экрана zb
вверх k, вверх экрана H
вверх экрана zt
вправо l

в середину экрана M
в середину экрана zz z.

 

"Глобальные" перемещения:

gg идем в начало;
G
идем в самый конец;
50%
идем в середину всего документа (50% же);
:50
идем на 50-ю строку;

  ctrl-o, ctrl-i перемещаемся вперед-назад по сохраненной истории координат курсора. Не все положения курсора сохраняються в этой истории. Сохраняються координаты, в которые мы попадали в результате перемещения, которое классифицируется как "прыжок". Список сохраненных прыжков можно увидеть командой :jumps. Очень полезная фишка. Вам не нужно бегать по файловому менеджеру в поисках файла, который вы уже когда-то редактировали. Просто войдите в vim в нормальном режиме и понажимайте  ctrl-o, ctrl-i.

Строчные перемещения:

0
переходим в самое начало строки;
^
переходим в на первый непробельный символ строки;
$
переходим в самый конец строки;
-
переходим на начало строки ввех;
+
переходим на начало строки вниз;

Пословные перемещения:

w
переход к началу следующего слова;
W
переход к началу следущего слова;
e
переход к следующему концу слова;
E
переход к следующему концу слова;
b
переход к началу предыдущего слова;
B
переход к началу предыдущего слова;
tx, fx
переход к следующей букве х;
Tх, Fх
переход к предыдущей букве х;
;
повторить предыдущий tfTF-переход;

Ну а про эти команды мне даже как-то неудобно и говорить:
mx установить закладку х;
'x перейти к закладке х;
''
переходим к предыдущему месту, откуда делали последний переход к закладке;
:marks смотрим какие у нас закладки;

Учите клавиатуру. Воздастся. ))


среда, 5 мая 2021 г.

Конфигурирование связки SPF, DKIM, DMARC записей и sendmail на debian

    Что такое SPF, DKIM, DMARC объяснять не буду. Раз вы это читаете, то вы уже знаете, что это или хотя бы зачем. Тут просто расскажу как я это установил на debian buster c почтовым сервером sendmail. 

#apt install opendkim opendkim-tools

#vim /etc/opendkim.conf

Syslog                         yes
SyslogSuccess            Yes
LogWhy                      Yes
AutoRestart                Yes
AutoRestartRate        10/1h
UMask                        002
Canonicalization        relaxed/simple
Mode                          sv
Socket                        inet:8891@127.0.0.1
PidFile                       /var/run/opendkim/opendkim.pid
SignatureAlgorithm   rsa-sha256
UserID                       opendkim:opendkim
Domain                       my.domain.ua
Selector                      mymail

OversignHeaders       From
TrustAnchorFile         /usr/share/dns/root.key
ExternalIgnoreList     refile:/etc/opendkim/TrustedHosts
InternalHosts             refile:/etc/opendkim/TrustedHosts
KeyTable                     refile:/etc/opendkim/KeyTable

SigningTable               refile:/etc/opendkim/SigningTable

Обратите внимание на пять строчек, выделенных жирным шрифтом. Их понимание, на мое мнение, особенно важно для того, что мы хотим сделать.

вместо my.domain.ua подставим вывод команды  (на почовом сервере)

#hostname -d

вместо mymail подставим вывод команды (также на почтовике) 

#hostname 

Далее создаем директорию, где находится пара приватный-публичный ключи:

#mkdir -p /etc/opendkim/keys

И создаем собственно эти ключи:

#opendkim-genkey -D /etc/opendkim/keys -d my.domain.ua  -s mymail
#chown -R opendkim:opendkim /etc/opendkim/keys
#chmod 640 /etc/opendkim/keys/mymail.private
#chmod 644 /etc/opendkim/keys/mymail.txt

mymail.private - здесь хранится приватный ключ, доступ к которому имеет только почтовый демон (в нашем случае - сендмейл).
mymail.txt - здесь хранится публичный ключ, который мы впишем в файл зоны днс.
    Далее создаем и редактируем три таких файла:

#vim /etc/opendkim/KeyTable

mymail._domainkey.my.domain.ua my.domain.ua:mymail:/etc/opendkim/keys/mymail.private
Здесь мы говорим мы говорим мильтеру opendkim, а значит сендмейлу, где находится приватный ключ.

#vim /etc/opendkim/SigningTable

*@my.domain.ua mymail._domainkey.my.domain.ua
Здесь мы говорим какие почтовые адреса соответствуют dkim-записи в днс.

#vim /etc/opendkim/TrustedHosts

127.0.0.1
localhost
my.domain.ua
Тут имя файла говорит само за себя.
 
    Рестартуем опендким и смотрим, слушает ли он порт, что мы прописали в конфиге выше:
#systemctl restart opendkim
#netstat -nlp | grep 8891
tcp        0      0 127.0.0.1:8891          0.0.0.0:*               LISTEN      23116/opendkim

    Расскажем сендмейлу, что у него появился новый мильтер (о мильтере можно думать как о некоем плагине, который подсоединяется к главному процессу сендмейла с помощью специального API - milter API). Редактируем текстовый конфиг сендмейла:
#vim /etc/mail/sendmail.mc
...

INPUT_MAIL_FILTER(`milter1', `...')dnl
INPUT_MAIL_FILTER(`milter2', `...')dnl
INPUT_MAIL_FILTER(`opendkim', `S=inet:8891@127.0.0.1')dnl
define(`confINPUT_MAIL_FILTERS',`opendkim,milter1,milter2')dnl
...

milter1 и milter2 вписаны для примера. Их может и не быть или может быть больше двух. У меня, например, к сендмейлу подсоединены два мильтера кламава и спамассассина. Третий стало быть опендким. Обратите внимание на `S=inet:8891@127.0.0.1' - эта запись должна совпадать с тем, что мы прописали в конфиге выше.

Выполняем

#sendmailconfig
#systemctl restart sendmail 

Осталось отконфигурировать файл прямой зоны авторитетного днс для домена, который обслуживает наш почтовый сервер:

$ORIGIN         my.domain.ua.
@                     IN SOA  mydns.my.domain.ua. hostm.my.domain.ua. ( ... )
                        IN      NS      mydns.my.domain.ua.
                        IN      MX      10 mymail.my.domain.ua.
@                     IN      TXT     "v=spf1 +mx +a ip4:11.11.11.11 -all"
mymail._domainkey        IN    TXT    "v=DKIM1\; h=sha256\; k=rsa\; p=""xxxx""xxxx""xxxx""xxxx" 
_adsp._domainkey        IN    TXT    "dkim=all"
_dmarc                        IN    TXT    "v=DMARC1\; p=reject\; rua=mailto:hostm@my.domain.ua"

    11.11.11.11 - айпишник нашего почтовика. Строка

mymail._domainkey        IN      TXT     "v=DKIM1\; h=sha256\; k=rsa\; p=""xxxx""xxxx""xxxx""xxxx"

полностью соответствует содержимому файла mymail.txt, что мы создали ранее. ххххххх - так обозначены цифры ключа. Есть пара нюансов. Точки с запятой в файле днс соответствуют началу комментариев. Поэтому мы их экранируем слэшем. Также, строки в файле зоны днс не могут быть длиннее кажется 256 символов, что меньше, чем длина нашей строки (строка, что начинается из v= ), из-за чего эту длинную строку надо разбить несколькими вставками "" - они обозначены синим цветом. Также длинные строки можно заключать в круглые скобки, но у меня почему-то это не работало.

    Строка с v=spf1 и есть spf запись. Строка с v=DKIM1 и есть dkim запись. Строка с v=DMARC1 и есть dmarc запись. Две строки, начинающиеся с _adsp и _dmarc просят всех не принимать письма без DKIM-подписи нашего домена. Собственно для этого все это и затевалось.

    Теперь при отсылке почты в логах почтовика появяться соответствующие строки:

# journalctl -u opendkim

DKIM-Signature field added (s=mymail, d=my.domain.ua)

    Также в служебной информации писем появяться DKIM-подписи вашего домена.

    Я б рекомендовал бы вот эту ссылку , которая была основой данного изложения, но надеюсь, здесь все написано достаточно точно.

воскресенье, 17 января 2021 г.

Sendmail: анализ логов

    Отчаявшись найти хотя б даже и неинтуитивный анализатор логов почтового сервера на Sendmail и Dovecot, пришлось осилить несколько строк командной оболочки. Дело было в Дебиан.

#journalctl --since yesterday -u sendmail|grep ctladdr|grep Sent|awk -F: '{print $5}'|grep -Po 'ctladdr=<\K[^>]*' 

Это смотрим отправщиков писем.

#ls -al /var/mail/|awk '{printf("%-20s%s\n", $3,$5)}'

Это анализируем почтовые ящики.

#journalctl -u dovecot|grep pop3-login|egrep -v Disconnected|awk -F, '{print $1}'|awk -v RS='<' -v FS='>' 'NR>1{print $1}'

Это анализируем читателей писем.


sort|uniq -c|sort -nk1 такое добавляем в конец пайпа для сортировки вывода по числам в первом столбце.

awk '{sum+=$2} END {print $2}' такого типа команду добавляем в конец пайпа, чтоб просуммировать числа во втором столбце.

Ну и напоследок формат по умолчанию записи в журнал сендмейлом в случае если отсылка мыла произошла успешно (схематично):

Date Time MyServerName sm-mta[x]: 10HF0kYP020730: from=<g@g.org>, size=x, class=0, nrcpts=1, msgid=<xxx>, proto=ESMTPSA, daemon=MTA, relay=server.domen [x.x.x.x]
Date Time MeServerName sm-mta[x]: 10HF0kYP020730: to=<g2@g2.org>, ctladdr=<g@g.org> (1254/1498), delay=00:00:02, xdelay=00:00:02, mailer=esmtp, pri=120563, relay=x.net. [x.x.x.x], dsn=2.0.0, stat=Sent (ok dirdel)