Раз вспомнили про Synology DSM в Одновременное монтирование зашифрованных папок в Synology DSM, то решил показать как при помощи Perl и веб фреймворка Mojolicious сделать простое приложение под свои нужды. Мне пришлось написать на коленке утилиту SynDevInfo, для вывода необходимой информации по устройству в браузере, т.к. уважаемый support не знает GNU/Linux, а пользователи не могут зайти по ssh.


Под катом будет описана структура пакета, скрипт запуска приложения и сам скрипт на Mojolicious::Lite


Структура проекта


image
  • 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';
<pre>
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 %>
</pre>

@@ layouts/default.html.ep
<!DOCTYPE html>
<html>
  <head>
    <!-- Latest compiled and minified CSS -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7" crossorigin="anonymous">

    <!-- Optional theme -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap-theme.min.css" integrity="sha384-fLW2N01lMqjakBkx3l/M9EahuwpSfeNvV63J5ezn3uZzapT0u7EYsXMjQV+0En5r" crossorigin="anonymous">

    <!-- Latest compiled and minified JavaScript -->
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js" integrity="sha384-0mSbJDEHialfmuBBQP6A4Qrprq5OVfW37PRR3j5ELqxss1yVqOtnepnHVP9aJ7xS" crossorigin="anonymous"></script>
    <style type="text/css">
    section{
      background: #fff;
      border-radius: 7px 7px 7px 7px;
      -moz-border-radius: 7px 7px 7px 7px;
      -webkit-border-radius: 7px 7px 7px 7px;
      border: 0px solid #000000;
      margin-bottom: 10px;
      padding: 27px 27px 20px;
    }
    </style>
    <title><%= title %></title>
   </head>
  <body>
    <section>
      <%= content %>
    </section>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
  </body>
</html>

В одном файле и код контроллера и html шаблоны. Перед запуском цикла обработки ввода вывода модель осуществляет сбор информации и сериализацию в json. В этот момент времени демон не обрабатывает внешние запросы, поэтому в скрипте запуска прописано ожидание 5 секунд. В хелпере эта модель обратно десериализуется и выводится в контроллере.


Запустив получаем такой результат
Поделиться с друзьями
-->

Комментарии (4)


  1. Fox_exe
    10.06.2016 15:30

    Я както делал аналогичное, но с помощью Bash скрипта, который работал в режиме CGI через httpd из состава Busybox'а. (Внутри скрипта — голый HTML и пара вызовов cat /proc/[cpuinfo|meminfo|partitions])
    Из ключевых «Фич» — не нужно вообще никаких «перлов» и прочего. Только веб-сервер с поддержкой CGI


    1. RPG18
      10.06.2016 15:38

      В прошлой статье по DSM был скрипт CGI на баше, который делал перенаправление на демон.


      1. Fox_exe
        10.06.2016 18:25
        +1

        Я немного не про это, а про то, что Perl тут не особо то и нужен, как и любые другие зависимости. Всё делается на баше (Ну или почти всё).


        1. RPG18
          11.06.2016 15:40

          Можно. Но мне удобнее использовать полноценный фреймворк.