diff --git a/AN/Tools.pm b/AN/Tools.pm index b578e082..dbc8cd5a 100755 --- a/AN/Tools.pm +++ b/AN/Tools.pm @@ -563,6 +563,9 @@ sub _set_paths 'scancore-daemon' => "/usr/sbin/striker/scancore-daemon", 'scancore-update-states' => "/usr/sbin/striker/scancore-update-states", }, + urls => { + skins => "/skins", + }, words => { 'an-tools.xml' => "/usr/share/perl5/AN/an-tools.xml", }, @@ -571,8 +574,8 @@ sub _set_paths # Make sure we actually have the requested files. foreach my $type (sort {$a cmp $b} keys %{$an->data->{path}}) { - # We don't look for logs because we'll create them if they don't exist. - next if $type eq "logs"; + # We don't look for urls because they're relative to the domain. + next if $type eq "urls"; foreach my $file (sort {$a cmp $b} keys %{$an->data->{path}{$type}}) { if (not -e $an->data->{path}{$type}{$file}) diff --git a/AN/Tools/Storage.pm b/AN/Tools/Storage.pm index 126260f3..54a8c96d 100755 --- a/AN/Tools/Storage.pm +++ b/AN/Tools/Storage.pm @@ -128,13 +128,13 @@ sub change_mode if (not $error) { my $shell_call = $an->data->{path}{exe}{'chmod'}." $mode $target"; - $an->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "log_0011", variables => { shell_call => $shell_call }}); + $an->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, key => "log_0011", variables => { shell_call => $shell_call }}); open (my $file_handle, $shell_call." 2>&1 |") or $an->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0014", variables => { shell_call => $shell_call, error => $! }}); while(<$file_handle>) { chomp; my $line = $_; - $an->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "log_0017", variables => { line => $line }}); + $an->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, key => "log_0017", variables => { line => $line }}); } close $file_handle; } @@ -174,7 +174,7 @@ sub change_owner my $target = defined $parameter->{target} ? $parameter->{target} : ""; my $group = defined $parameter->{group} ? $parameter->{group} : ""; my $user = defined $parameter->{user} ? $parameter->{user} : ""; - $an->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + $an->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { target => $target, group => $group, user => $user, @@ -201,13 +201,13 @@ sub change_owner if ((not $error) && ($string)) { my $shell_call = $an->data->{path}{exe}{'chown'}." $string $target"; - $an->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "log_0011", variables => { shell_call => $shell_call }}); + $an->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, key => "log_0011", variables => { shell_call => $shell_call }}); open (my $file_handle, $shell_call." 2>&1 |") or $an->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0014", variables => { shell_call => $shell_call, error => $! }}); while(<$file_handle>) { chomp; my $line = $_; - $an->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "log_0017", variables => { line => $line }}); + $an->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, key => "log_0017", variables => { line => $line }}); } close $file_handle; } @@ -329,7 +329,7 @@ sub make_directory my $group = defined $parameter->{group} ? $parameter->{group} : ""; my $mode = defined $parameter->{mode} ? $parameter->{mode} : ""; my $user = defined $parameter->{user} ? $parameter->{user} : ""; - $an->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + $an->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { directory => $directory, group => $group, mode => $mode, @@ -342,17 +342,19 @@ sub make_directory { next if not $directory; $working_directory .= "/$directory"; - if (-e $working_directory) + $working_directory =~ s/\/\//\//g; + $an->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { working_directory => $working_directory }}); + if (not -e $working_directory) { # Directory doesn't exist, so create it. my $shell_call = $an->data->{path}{exe}{'mkdir'}." ".$working_directory; - $an->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "log_0011", variables => { shell_call => $shell_call }}); + $an->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, key => "log_0011", variables => { shell_call => $shell_call }}); open (my $file_handle, $shell_call." 2>&1 |") or $an->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0014", variables => { shell_call => $shell_call, error => $! }}); while(<$file_handle>) { chomp; my $line = $_; - $an->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "log_0017", variables => { line => $line }}); + $an->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, key => "log_0017", variables => { line => $line }}); } close $file_handle; @@ -703,7 +705,7 @@ sub write_file my $mode = defined $parameter->{mode} ? $parameter->{mode} : ""; my $overwrite = defined $parameter->{overwrite} ? $parameter->{overwrite} : 0; my $user = defined $parameter->{user} ? $parameter->{user} : ""; - $an->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + $an->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { body => $body, file => $file, group => $group, @@ -731,24 +733,24 @@ sub write_file { # Break the directory off the file. my ($directory, $file_name) = ($file =~ /^(\/.*)\/(.*)$/); - $an->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + $an->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { directory => $directory, file_name => $file_name, }}); - if (not -d $directory) + if (not -e $directory) { + # Don't pass the mode as the file's mode is likely not executable. $an->Storage->make_directory({ directory => $directory, group => $group, - mode => $mode, user => $user, }); } # Now write the file. my $shell_call = $file; - $an->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "log_0013", variables => { shell_call => $shell_call }}); + $an->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, key => "log_0013", variables => { shell_call => $shell_call }}); open (my $file_handle, ">", $shell_call) or $an->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0016", variables => { shell_call => $shell_call, error => $! }}); print $file_handle $body; close $file_handle; diff --git a/AN/Tools/Template.pm b/AN/Tools/Template.pm index 1a995265..d74dbd0a 100755 --- a/AN/Tools/Template.pm +++ b/AN/Tools/Template.pm @@ -12,6 +12,7 @@ my $THIS_FILE = "Template.pm"; ### Methods; # get +# skin =pod @@ -125,7 +126,7 @@ sub get # If the user passed the skin, prepend the skins directory. Otherwise use the active skin. if (not $skin) { - $skin = $an->Template->skin; + $skin = $an->data->{path}{directories}{skins}."/".$an->Template->skin; $an->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { skin => $skin }}); } else @@ -211,10 +212,9 @@ sub get return($template); } - =head2 skin -This sets or returns the active skin used when rendering web output. The returned string is the full path to the skin directory, including the active skin name. +This sets or returns the active skin used when rendering web output. The default skin is set via 'C<< defaults::template::html >>' and it must be the same as the directory name under 'C<< /var/www/html/skins/ >>'. @@ -242,7 +242,7 @@ sub skin $an->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { skin_directory => $skin_directory }}); if (-d $skin_directory) { - $self->{SKIN}{HTML} = $skin_directory; + $self->{SKIN}{HTML} = $set; $an->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { 'SKIN::HTML' => $self->{SKIN}{HTML} }}); } else @@ -253,7 +253,7 @@ sub skin if (not $self->{SKIN}{HTML}) { - $self->{SKIN}{HTML} = $an->data->{path}{directories}{skins}."/".$an->data->{defaults}{template}{html}; + $self->{SKIN}{HTML} = $an->data->{defaults}{template}{html}; } return($self->{SKIN}{HTML}); diff --git a/AN/Tools/Words.pm b/AN/Tools/Words.pm index 922d5a54..1e291c15 100755 --- a/AN/Tools/Words.pm +++ b/AN/Tools/Words.pm @@ -265,8 +265,9 @@ sub read my $an = $self->parent; # Setup default values - my $file = defined $parameter->{file} ? $parameter->{file} : 0; my $return_code = 0; + my $file = defined $parameter->{file} ? $parameter->{file} : 0; + $an->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { file => $file }}); if (not $file) { @@ -283,7 +284,7 @@ sub read elsif (not -r $file) { # NOTE: Log the problem, do not translate. - $an->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", raw => "[ Error ] - Words->read()' asked to read: [$file] which was not readable by: [".getpwuid($<)."/".getpwuid($>)."] (uid/euid: [".$<."/".$>."])."}); + $an->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", raw => "[ Error ] - Words->read()' asked to read: [$file] which was not readable by: [".getpwuid($<)."] (uid/euid: [".$<."])."}); $return_code = 3; } else diff --git a/cgi-bin/home b/cgi-bin/home index 4012ef26..c45201f7 100755 --- a/cgi-bin/home +++ b/cgi-bin/home @@ -3,6 +3,7 @@ use strict; use warnings; use AN::Tools; +use Data::Dumper; my $THIS_FILE = ($0 =~ /^.*\/(.*)$/)[0]; my $running_directory = ($0 =~ /^(.*?)\/$THIS_FILE$/)[0]; @@ -25,21 +26,77 @@ $an->Words->read({file => $an->data->{path}{directories}{'cgi-bin'}."/words.xml" $| = 1; my $header = $an->Template->get({file => "main.html", name => "header", variables => { - language => $an->Words->language, - skin_directory => $an->Template->skin, + language => $an->Words->language, + skin_url => $an->data->{path}{urls}{skins}."/".$an->Template->skin, }}); +my $footer = $an->Template->get({file => "main.html", name => "footer"}); +my $network = get_network_details($an); my $template = $an->Template->get({file => "main.html", name => "master", variables => { header => $header, - left_top_bar => "", - center_top_bar => "", - right_top_bar => "", - center_body => "", - left_bottom_bar => "", - center_bottom_bar => "", - right_bottom_bar => "", - footer => "", + skin_url => $an->data->{path}{urls}{skins}."/".$an->Template->skin, + left_top_bar => " ", + center_top_bar => "$network", + right_top_bar => " ", + center_body => " ", + left_bottom_bar => " ", + center_bottom_bar => " ", + right_bottom_bar => " ", + footer => $footer, }}); print $template; exit(0); + +############################################################################################################# +# Functions # +############################################################################################################# + +# This reads the network status XML file and creates the template body. +sub get_network_details +{ + my ($an) = @_; + + my $file = $an->data->{path}{directories}{html}."/status/network.xml"; + $an->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { file => $file }}); + my $xml = XML::Simple->new(); + my $data = ""; + my $network = ""; + eval { $data = $xml->XMLin($file, KeyAttr => { interface => 'name', key => 'name' }, ForceArray => [ 'interface', 'key' ]) }; + if ($@) + { + chomp $@; + my $error = "[ Error ] - The was a problem reading: [$file]. The error was:\n"; + $error .= "===========================================================\n"; + $error .= $@."\n"; + $error .= "===========================================================\n"; + $an->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", raw => $error}); + } + else + { + my $interface_list = ""; + $network = $an->Template->get({file => "main.html", name => "network_header"}); + foreach my $interface (sort {$a cmp $b} keys %{$data->{interface}}) + { + $interface_list .= "$interface,"; + $network .= $an->Template->get({file => "main.html", name => "network_entry", variables => { + #mac => $data->{interface}{$interface}{mac}, + mac => "", + mac_id => $interface."_mac", + name => $interface, + name_id => $interface."_name", + #speed => $data->{interface}{$interface}{speed}, + speed => "", + speed_id => $interface."_speed", + #'link' => $data->{interface}{$interface}{'link'}, + 'link' => "", + link_id => $interface."_link", + }}); + } + $interface_list =~ s/,$//; + $network .= $an->Template->get({file => "main.html", name => "network_footer", variables => { interface_list => $interface_list }}); + } + + $an->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { network => $network }}); + return($network); +} diff --git a/cgi-bin/words.xml b/cgi-bin/words.xml index 45484c05..8bb1d2e5 100644 --- a/cgi-bin/words.xml +++ b/cgi-bin/words.xml @@ -17,6 +17,14 @@ This is the AN::Tools master 'words' file. Anvil! Striker ScanCore + Alteeve's Niche! Inc., Toronto, Ontario, Canada]]> + + Network Interfaces + MAC Address + Current Name + Link State + Link Speed (Mbps) + @@ -26,6 +34,7 @@ This is the AN::Tools master 'words' file. Anvil! ストライカ スカンコア + Alteeve's Niche! Inc., トロント、オンタリオ、カナダ]]> diff --git a/html/skins/alteeve/images/Texture.jpg b/html/skins/alteeve/images/Texture.jpg new file mode 100644 index 00000000..369b49c0 Binary files /dev/null and b/html/skins/alteeve/images/Texture.jpg differ diff --git a/html/skins/alteeve/images/logo.png b/html/skins/alteeve/images/logo.png new file mode 100644 index 00000000..01bbd1ff Binary files /dev/null and b/html/skins/alteeve/images/logo.png differ diff --git a/html/skins/alteeve/images/network-wired-connected.png b/html/skins/alteeve/images/network-wired-connected.png new file mode 100644 index 00000000..687c410d Binary files /dev/null and b/html/skins/alteeve/images/network-wired-connected.png differ diff --git a/html/skins/alteeve/images/network-wired-disconnected.png b/html/skins/alteeve/images/network-wired-disconnected.png new file mode 100644 index 00000000..7cf49e9c Binary files /dev/null and b/html/skins/alteeve/images/network-wired-disconnected.png differ diff --git a/html/skins/alteeve/images/sources.txt b/html/skins/alteeve/images/sources.txt new file mode 100644 index 00000000..84a8a791 --- /dev/null +++ b/html/skins/alteeve/images/sources.txt @@ -0,0 +1,3 @@ +From the gnome project; +- network-wired-connected.png +- network-wired-disconnected.png diff --git a/html/skins/alteeve/main.css b/html/skins/alteeve/main.css index 7d342311..bf21aa16 100644 --- a/html/skins/alteeve/main.css +++ b/html/skins/alteeve/main.css @@ -1,14 +1,81 @@ /* Colours; -- Darkest Gray: #343434 -- Logo Gray: #9ba0a5 -- Lightest Gray: #f7f7f7 -- Font Dark Gray: #444444 -- Font Light Gray: #f2f2f2 -- Alteeve Red: #d02724 +- Darkest Gray: #343434 +- Logo Gray: #9ba0a5 +- Lightest Gray: #f7f7f7 +- Font Dark Gray: #444444 +- Font Light Gray: #f2f2f2 +- Alteeve Red: #d02724 +- Footer background: #171717 +- Footer text: #515151 */ body { - background-image: url("skins/alteeve/images/Texture.jpg"); - background-repeat: repeat-y; + font-family: 'Dejavu Sans', Arial, Helvetica, Verdana, Sans-Serif; + background-image: url("/skins/alteeve/images/Texture.jpg"); + background-repeat: repeat; + color: #f2f2f2; +} + +.body_table { + width: 90%; + border: 0.1em solid blue; + margin: auto; + top: 0; + position: absolute; + left: 5% +} + +table { + border-spacing: 0; + border-collapse: collapse; +} +td { + border: 1px solid green; + padding: 0; + border-collapse: collapse; +} + +.logo { + height: 2.5em; +} + +.header { + text-align: center; + background: #343434; + color: #f2f2f2; + border-bottom: 0.2em solid #d02724; +} +.header a:link, .header a:visited { + cursor: pointer; + color: #f6f6f6; + text-decoration: none; +} +.header a:hover { + cursor: pointer; + color: #f6f6f6; + text-decoration: none; +} + +.footer { + text-align: center; + background: #171717; + font-size: 12px; + color: #515151; + width: 90%; + position: fixed; + bottom: 0; + left: 5%; +} + +.footer a:link, .footer a:visited { + cursor: pointer; + text-decoration: none; + color: #515151; +} + +.footer a:hover { + cursor: pointer; + text-decoration: none; + color: #616161; } diff --git a/html/skins/alteeve/main.html b/html/skins/alteeve/main.html index 03286ee9..0d06e7e6 100644 --- a/html/skins/alteeve/main.html +++ b/html/skins/alteeve/main.html @@ -1,7 +1,41 @@ + + + + + + + + + + + + + + + + + + + #!string!brand_0001!# - #!string!brand_0003!# + + + + + + + #!variable!header!# - + +
+ + +
+ +
#!variable!left_top_bar!# @@ -30,21 +64,53 @@
+ #!variable!footer!# + - - - - - - #!string!brand_0001!# - #!string!brand_0003!# - - - - - + + + + - - + + + + #!variable!mac!# + + + #!variable!name!# + + + #!variable!link!# + + + #!variable!speed!# + + + + + + + + + + + + + + + + diff --git a/html/skins/alteeve/main.js b/html/skins/alteeve/main.js index e69de29b..ce2ac422 100644 --- a/html/skins/alteeve/main.js +++ b/html/skins/alteeve/main.js @@ -0,0 +1,22 @@ +$(function() { + var say_up = "Up"; + var say_down = "Down"; + if($("#network_status").length) { + //alert('network status exists.'); + $.getJSON('/status/network.json', { get_param: 'value' }, function(data) { + $.each(data.networks, function(index, element) { + console.log('entry: ['+index+'], name: ['+element.name+'], mac: ['+element.mac+'], link: ['+element.link+']'); + var link = say_up; + if (element.link == 0) { + link = say_down; + } + $("#"+element.name+"_mac").text(element.mac); + $("#"+element.name+"_link").text(link); + }); + }); + } + if($("#disk_status").length) { + //alert('disk status exists.'); + //$("#bar").text('B'); + } +}); diff --git a/tools/scancore-daemon b/tools/scancore-daemon index 767a477b..0816338a 100755 --- a/tools/scancore-daemon +++ b/tools/scancore-daemon @@ -24,7 +24,7 @@ while(1) { update_state_file($an); - sleep 10; + sleep 2; } exit(0); @@ -40,12 +40,12 @@ sub update_state_file my ($an) = @_; my $shell_call = $an->data->{path}{tools}{'scancore-update-states'}; - $an->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "log_0011", variables => { shell_call => $shell_call }}); + $an->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, key => "log_0011", variables => { shell_call => $shell_call }}); open (my $file_handle, $shell_call." 2>&1 |") or $an->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0014", variables => { shell_call => $shell_call, error => $! }}); while(<$file_handle>) { chomp; - $an->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "log_0017", variables => { output => $_ }}); + $an->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, key => "log_0017", variables => { output => $_ }}); } close $file_handle; diff --git a/tools/scancore-update-states b/tools/scancore-update-states index bfc085a0..040562a8 100755 --- a/tools/scancore-update-states +++ b/tools/scancore-update-states @@ -20,36 +20,7 @@ $an->Log->level(2); # Turn off buffering so that the pinwheel will display while waiting for the SSH call(s) to complete. $| = 1; - -# my $shell_call = "/sys/class/net/ens11/address"; -# $an->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "log_0012", variables => { shell_call => $shell_call }}); -# open (my $file_handle, "<$shell_call") or $an->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0015", variables => { shell_call => $shell_call, error => $! }});; -# while(<$file_handle>) -# { -# chomp; -# my $line = $_; -# $an->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "log_0023", variables => { output => $line }}); -# print "line: [$line]\n"; -# } -# close - -my $status = "\n"; - $status .= "\n"; - $status .= report_network($an); - $status .= "\n"; - -### TODO: Set the 'status.xml' name into 'striker.conf' -# Write the file. -my $output_file = $an->data->{path}{directories}{html}."/status.xml"; -$an->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { output_file => $output_file }}); -$an->Storage->write_file({ - file => $output_file, - body => $status, - overwrite => 1, - mode => "0644", - user => "apache", - group => "apache" -}); +report_network($an); exit(0); @@ -62,7 +33,11 @@ sub report_network { my ($an) = @_; - my $network = " \n"; + # Write out the data in json format. + my $network_json = "{\"networks\":[\n"; + my $network_xml = "\n"; + $network_xml .= "\n"; + my $directory = $an->data->{path}{sysfs}{network_interfaces}; local(*DIRECTORY); $an->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, key => "log_0018", variables => { directory => $directory }}); @@ -93,12 +68,42 @@ sub report_network operational => $operational, speed => $speed, }}); - $network .= " \n"; + + $network_json .= " { \"name\":\"$interface\", \"mac\":\"$mac_address\", \"link\":\"$link_state\", \"duplex\":\"$duplex\", \"state\":\"$operational\", \"speed\":\"$speed\" },\n"; + $network_xml .= " \n"; } } closedir(DIRECTORY); - $network .= " \n"; + $network_json =~ s/,$//s; + $network_json .= "]}\n"; + $an->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { network_json => $network_json }}); + + $network_xml .= "\n"; + $an->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { network_xml => $network_xml }}); + + ### TODO: Set the 'status/network.xml' and 'status/network.json' names into 'striker.conf' + # Write the JSON file. + my $output_json = $an->data->{path}{directories}{html}."/status/network.json"; + $an->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { output_xml => $output_json }}); + $an->Storage->write_file({ + file => $output_json, + body => $network_json, + overwrite => 1, + mode => "0644", + user => "apache", + group => "apache" + }); + # Write the XML file. + my $output_xml = $an->data->{path}{directories}{html}."/status/network.xml"; + $an->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { output_xml => $output_xml }}); + $an->Storage->write_file({ + file => $output_xml, + body => $network_xml, + overwrite => 1, + mode => "0644", + user => "apache", + group => "apache" + }); - $an->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { network => $network }}); - return($network); + return(0); } diff --git a/units/scancore-daemon.service b/units/scancore-daemon.service index ba0e854c..4b60081d 100644 --- a/units/scancore-daemon.service +++ b/units/scancore-daemon.service @@ -1,5 +1,5 @@ [Unit] -Description=ScanCore daemon used to handle several jobs as part of the Anvil! IA suite +Description=ScanCore - Anvil! IA Suite Wants=network.target [Service]
+ #!string!header_0001!# +
+ #!string!header_0002!# + + #!string!header_0003!# + + #!string!header_0004!# + + #!string!header_0005!# +