Synology DSM, Perl и Mojolicious
Раз вспомнили про Synology DSM в Одновременное монтирование зашифрованных папок в Synology DSM, то решил показать как при помощи Perl и веб фреймворка Mojolicious сделать простое приложение под свои нужды. Мне пришлось написать на коленке утилиту SynDevInfo, для вывода необходимой информации по устройству в браузере, т.к. уважаемый support не знает GNU/Linux, а пользователи не могут зайти по ssh.
Под катом будет описана структура пакета, скрипт запуска приложения и сам скрипт на Mojolicious: Lite
Структура проекта
- 3rdparty туда поместил Mojolicious и IO: Socket: IP. IO: Socket: IP был необходим Mojolicious;
- conf специальный каталог для пакета DSM. В PKG_DEPS прописана зависимость от Perl;
- lib содержит код модельки SynInfo: Info;
- scripts обязательный набор скриптов входящих в пакет;
- ui конфигурационный файл и иконки, для интеграции в UI;
- INFO мета информация пакета;
- Makefile сценарий сборки пакета;
- SynDevInfo сам скрипт.
Запуск приложения
Mojolicious поддерживает кучу разных способов разворачивания приложения. Пользуясь принципом: простые вещи должны делаться просто, воспользовался подходом описанным в Built-in web server. При таком способе скрипт запускается в режиме демона и не требует внешнего web сервера.
#!/bin/sh
export PERL5LIB="${SYNOPKG_PKGDEST}/3rdparty"
case $1 in
start)
/usr/bin/perl ${SYNOPKG_PKGDEST}/SynDevInfo daemon -m production -l http://*:12345 &
sleep 5;
exit 0
;;
stop)
PID=`ps -aef | grep "SynDevInfo" | grep -v grep | awk '{print $2}'`
kill -9 $PID
exit 0
;;
restart)
exit 0
;;
status)
exit 0
;;
log)
exit 0
;;
esac
Приложение
#!/usr/bin/env perl
use strict;
use utf8;
use feature ':5.14';
use warnings FATAL => 'all';
use FindBin;
BEGIN { unshift @INC, "$FindBin::Bin/lib" }
use Mojolicious::Lite;
use SynInfo::Info;
my $CACHE_FILE = "/tmp/syninfo_cashe.json";
get '/' => sub {
my $c = shift;
$c->render(template => 'index',
syno_def => $c->syn_info->syno_defines(),
general => $c->syn_info->general(),
volumes => $c->syn_info->volume(),
networks=> $c->syn_info->networks(),
uname => $c->syn_info->uname(),
cpuinfo => $c->syn_info->cpuinfo()
);
};
helper syn_info => sub { state $info = SynInfo::Info->load($CACHE_FILE) };
my $info = SynInfo::Info->new();
$info->save($CACHE_FILE);
app->start;
__DATA__
@@ index.html.ep
% layout 'default';
% title 'Summary info';
PRODUCT: <%= $syno_def->{product} %>
MODEL: <%= $general->{model} %>
UNIQUE: <%= $syno_def->{unique} %>
FIRMWARE: <%= $general->{firmware_ver} %>(<%= $general->{firmware_date} %>)
CPU: <%= $general->{cpu_vendor} %> <%= $general->{cpu_family} %> <%= $general->{cpu_series} %> <%= $general->{cpu_clock_speed} %> Mhz, cores <%= $general->{cpu_cores} %>
RAM: <%= $general->{ram_size} %> Mb
<% foreach my $volume (@$volumes){ %>
VOLUME: <%= $volume->{name} %>
SIZE: <%= $volume->{total_size} %> Byte
USED: <%= $volume->{used_size} %> Byte<% } %>
<% foreach my $nif (@{$networks->{'nif'}}){ %>
Interface: <%= $nif->{id} %>
ADDRESS: <%= $nif->{addr} %>
TYPE: <%= $nif->{type} %>
<% } %>
Name and information about current kernel:
<% foreach my $key (sort keys %$uname){ chomp($uname->{$key}); %>
<%= $key %>: <%= $uname->{$key}%> <% } %>
cat /proc/cpuinfo
<%= $cpuinfo %>
@@ layouts/default.html.ep
<%= title %>
<%= content %>
В одном файле и код контроллера и html шаблоны. Перед запуском цикла обработки ввода вывода модель осуществляет сбор информации и сериализацию в json. В этот момент времени демон не обрабатывает внешние запросы, поэтому в скрипте запуска прописано ожидание 5 секунд. В хелпере эта модель обратно десериализуется и выводится в контроллере.