File Coverage

blib/lib/Message/Passing/Role/Script.pm
Criterion Covered Total %
statement 27 63 42.8
branch 0 24 0.0
condition 0 2 0.0
subroutine 9 13 69.2
pod 4 4 100.0
total 40 106 37.7


line stmt bran cond sub pod time code
1             package Message::Passing::Role::Script;
2 3     3   6781 use Moo::Role;
  3         8  
  3         18  
3 3     3   959 use MooX::Options;
  3         9  
  3         22  
4 3     3   2334 use MooX::Types::MooseLike::Base qw/ Bool Str /;
  3         8  
  3         223  
5 3     3   22 use Getopt::Long qw(:config pass_through);
  3         10  
  3         28  
6 3     3   667 use POSIX qw(setuid setgid);
  3         9  
  3         27  
7 3     3   5633 use Message::Passing::DSL;
  3         9  
  3         257  
8 3     3   44 use Carp qw/ confess /;
  3         7  
  3         191  
9 3     3   21 use namespace::clean -except => 'meta';
  3         6  
  3         24  
10              
11             requires 'build_chain';
12              
13             option daemonize => (
14             is => 'ro',
15             isa => Bool,
16             default => sub { 0 },
17             doc => 'pass 1 to daemonize',
18             );
19              
20             option io_priority => (
21             isa => sub { $_[0] =~ /^(none|be|rt|idle)$/ },
22             coerce => sub { lc $_[0] },
23             is => 'ro',
24             predicate => "_has_io_priority",
25             format => 's',
26             doc => 'the IO priority to run the script at: none, be, rt, idle',
27             );
28              
29             option user => (
30             isa => Str,
31             is => 'ro',
32             predicate => "_has_user",
33             format => 's',
34             doc => 'changes the user the script is running as',
35             );
36              
37             option pid_file => (
38             isa => Str,
39             is => 'ro',
40             predicate => "_has_pid_file",
41             format => 's',
42             doc => 'the name of the pid file including the directory',
43             );
44              
45             sub daemonize_if_needed {
46 0     0 1   my ($self) = @_;
47 0           my $fh;
48 0 0         if ($self->_has_pid_file) {
49 0 0         open($fh, '>', $self->pid_file)
50             or confess("Could not open pid file '". $self->pid_file . "': $?");
51             }
52 0 0         if ($self->daemonize) {
53 0 0         fork && exit;
54 0           POSIX::setsid();
55 0 0         fork && exit;
56 0           chdir '/';
57 0           umask 0;
58             }
59 0 0         if ($fh) {
60 0           print $fh $$ . "\n";
61 0           close($fh);
62             }
63             }
64              
65             sub change_uid_if_needed {
66 0     0 1   my $self = shift;
67 0           my ($uid, $gid);
68 0 0         if ($self->_has_user) {
69 0           my $user = $self->user;
70 0   0       $uid = getpwnam($user) ||
71             die("User '$user' does not exist, cannot become that user!\n");
72 0           (undef, undef, undef, $gid ) = getpwuid($uid);
73             }
74 0 0         if ($gid) {
75 0 0         setgid($gid) || die("Could not setgid to '$gid' are you root? : $!\n");
76             }
77 0 0         if ($uid) {
78 0 0         setuid($uid) || die("Could not setuid to '$uid' are you root? : $!\n");
79             }
80             }
81              
82             sub set_io_priority_if_needed {
83 0     0 1   my $self = shift;
84 0 0         return unless $self->_has_io_priority;
85 0           require Linux::IO_Prio;
86 0           my $sym = do {
87 3     3   3443 no strict 'refs';
  3         38  
  3         687  
88 0           &{"Linux::IO_Prio::IOPRIO_CLASS_" . uc($self->io_priority)}();
  0            
89             };
90 0           Linux::IO_Prio::ioprio_set(Linux::IO_Prio::IOPRIO_WHO_PROCESS(), $$,
91             Linux::IO_Prio::IOPRIO_PRIO_VALUE($sym, 0)
92             );
93             }
94              
95             sub start {
96 0     0 1   my $class = shift;
97 0           my $instance = $class->new_with_options(@_);
98 0           $instance->set_io_priority_if_needed;
99 0           $instance->change_uid_if_needed;
100 0           $instance->daemonize_if_needed;
101 0           run_message_server $instance->build_chain;
102             }
103              
104             1;
105              
106             =head1 NAME
107              
108             Message::Passing::Role::Script - Handy role for building messaging scripts.
109              
110             =head1 SYNOPSIS
111              
112             # my_message_passer.pl
113             package My::Message::Passer;
114             use Moo;
115             use MooX::Options;
116             use MooX::Types::MooseLike::Base qw/ Bool /;
117             use Message::Passing::DSL;
118              
119             with 'Message::Passing::Role::Script';
120              
121             option foo => (
122             is => 'ro',
123             isa => Bool,
124             );
125              
126             sub build_chain {
127             my $self = shift;
128             message_chain {
129             input example => ( output_to => 'test_out', .... );
130             output test_out => ( foo => $self->foo, ... );
131             };
132             }
133              
134             __PACKAGE__->start unless caller;
135             1;
136              
137             =head1 DESCRIPTION
138              
139             This role can be used to make simple message passing scripts.
140              
141             The user implements a L<MooX::Options> type script class, with a
142             C<build_chain> method, that builds one or more
143             L<Message::Passing> chains and returns them.
144              
145             __PACKAGE__->start unless caller;
146              
147             is then used before the end of the script.
148              
149             This means that when the code is run as a script, it'll parse
150             the command line options, and start a message passing server..
151              
152             =head1 REQUIRED METHODS
153              
154             =head1 build_chain
155              
156             Return a chain of message processors, or an array reference with
157             multiple chains of message processors.
158              
159             =head1 ATTRIBUTES
160              
161             =head2 daemonize
162              
163             Do a double fork and lose controlling terminal.
164              
165             Used to run scripts in the background.
166              
167             =head2 io_priority
168              
169             The IO priority to run the script at..
170              
171             Valid values for the IO priority are:
172              
173             =over
174              
175             =item none
176              
177             =item be
178              
179             =item rt
180              
181             =item idle
182              
183             =back
184              
185             =head2 user
186              
187             Changes the user the script is running as. You probably need to run the script as root for this option to work.
188              
189             =head2 pid_file
190              
191             Write a pid file out. Useful for running Message::Passing scripts as daemons and/or from init.d scripts.
192              
193             =head1 METHODS
194              
195             =head2 start
196              
197             Called as a class method, it will build the current class as a
198             command line script (parsing ARGV), setup the daemonization options,
199             call the ->build_chain method supplied by the user to build the
200             chains needed for this application.
201              
202             Then enters the event loop and never returns.
203              
204             =head2 change_uid_if_needed
205              
206             Tries to change uid if the --user option has been supplied
207              
208             =head2 daemonize_if_needed
209              
210             Tires to daemonize if the --daemonize option has been supplied
211              
212             =head2 set_io_priority_if_needed
213              
214             Tries to set the process' IO priority if the --io_priority option
215             has been supplied.
216              
217             =head1 SPONSORSHIP
218              
219             This module exists due to the wonderful people at Suretec Systems Ltd.
220             <http://www.suretecsystems.com/> who sponsored its development for its
221             VoIP division called SureVoIP <http://www.surevoip.co.uk/> for use with
222             the SureVoIP API -
223             <http://www.surevoip.co.uk/support/wiki/api_documentation>
224              
225             =head1 AUTHOR, COPYRIGHT AND LICENSE
226              
227             See L<Message::Passing>.
228              
229             =cut