disorder-notify.in, disorder-ondemand.in: Make sure `$buffer' is defined.
[disorder-toys] / disorder-ondemand.in
CommitLineData
8f6b6ec6
MW
1#! @PERL@ -w
2
3use autodie qw{:all};
4use strict;
5
6use DisOrder;
7use Getopt::Long qw{:config gnu_compat bundling
8 require_order no_getopt_compat};
9use POSIX qw{:errno_h :fcntl_h :sys_wait_h};
10
11my $CONFFILE = undef;
12my $DEBUG = 0;
13(my $PROG = $0) =~ s:^.*/::;
14
15sub mumble ($) { print STDERR ";; $_[0]\n" if $DEBUG; }
16
17sub connection () {
18 my $file = $CONFFILE // "$ENV{HOME}/.disorder/passwd", my $conf;
19
20 if (-f $file || defined $CONFFILE)
21 { $conf = load_config $file; }
22 else {
23 $conf = { connect => ["-unix", "/var/lib/disorder/private/socket"],
24 username => ["root"], password => ["hunoz"] };
25 }
26 return connect_to_server %$conf;
27}
28
29my $PAUSETIME = 300;
8537d8f8
MW
30my $CRASHTIME = 10;
31my $RESTARTTIME = 0;
8f6b6ec6
MW
32my $PID = undef;
33my $STATE = "off"; # `off', `on', `killed'
34my $WANT = "off"; # `off', `on', `pause'
86376913 35my $WAKETIME = undef;
8f6b6ec6
MW
36my $NOW = time;
37
38$SIG{CHLD} = sub {
8537d8f8 39 $NOW = time;
8f6b6ec6
MW
40 KID: for (;;) {
41 my $kid = waitpid -1, WNOHANG;
42 last if $kid <= 0;
43 if ($kid == $PID) {
44 mumble "player exited (st = $?)";
45 $PID = undef; $STATE = "off";
8537d8f8 46 if ($WANT eq "on" && $RESTARTTIME > $NOW) { $WAKETIME = $RESTARTTIME; }
8f6b6ec6
MW
47 }
48 }
49};
50
51$SIG{TERM} = $SIG{INT} = sub {
52 kill "TERM", $PID if defined $PID;
53 exit 0;
54};
55
8537d8f8
MW
56sub start () {
57 if ($WANT ne "on") {
58 $WANT = "on";
59 $WAKETIME = $NOW < $RESTARTTIME ? $RESTARTTIME : undef;
60 }
61}
8f6b6ec6
MW
62
63sub pause () {
86376913 64 if ($WANT eq "on") { $WANT = "pause"; $WAKETIME = $NOW + $PAUSETIME; }
8f6b6ec6
MW
65}
66
67sub fix_state () {
68
69 ##mumble "state = $STATE ($PID)";
86376913 70 ##mumble "want = $WANT ($WAKETIME <=> $NOW)";
8f6b6ec6 71
86376913
MW
72 if ($WANT eq "pause" && $NOW >= $WAKETIME) {
73 $WANT = "off"; $WAKETIME = undef;
8f6b6ec6
MW
74 mumble "pause time up: stopping" if $STATE eq "on";
75 }
76
8537d8f8
MW
77 if ($WANT eq "on" && $STATE eq "off" &&
78 (!defined($WAKETIME) || $NOW >= $WAKETIME)) {
8f6b6ec6
MW
79 my $kid = fork();
80 if (!$kid) { exec @ARGV; }
8537d8f8 81 $STATE = "on"; $PID = $kid; $RESTARTTIME = $NOW + $CRASHTIME;
8f6b6ec6
MW
82 mumble "player wanted but not running: started pid $kid";
83 } elsif ($WANT eq "off" && $STATE eq "on") {
84 kill "TERM", $PID;
85 $STATE = "killed";
86 mumble "player running but not wanted: killed pid $PID";
87 }
88}
89
90sub watch_status () {
91 my $sk = connection;
92
93 my $pause, my $track = 0;
94
95 my $rdin = ""; vec($rdin, fileno($sk), 1) = 1;
96 my $buffer = "", my @lines = ();
97
98 $NOW = time;
99
100 my $r = send_command $sk, "playing";
101 $track = defined $r;
102
103 print $sk "log\n";
104 fcntl $sk, F_SETFL, (fcntl $sk, F_GETFL, 0) | O_NONBLOCK;
105 WATCH: for (;;) {
106
107 if (!$sk) { mumble "eof from server"; last WATCH; }
108 my $nfd;
109 SEL: {
110 eval {
111 $nfd = select my $rdout = $rdin, undef, undef,
86376913 112 defined($WAKETIME) ? $WAKETIME - $NOW : 60;
8f6b6ec6
MW
113 };
114 if ($@ && $@->errno == EINTR) { next SEL; }
115 elsif ($@) { mumble "error from select: " . $@->errno; last WATCH; }
116 }
117 if (!$nfd) {
118 eval { print $sk "."; flush $sk; };
119 if ($@) { mumble "error from write: " . $@->errno; last WATCH; }
120 @lines = ();
121 } else {
122 READ: for (;;) {
123 my ($b, $n);
124 eval { $n = sysread $sk, $b, 4096; };
125 if ($@ && $@->errno == EAGAIN) { last READ; }
126 elsif ($@ && $@->errno == EINTR) { next READ; }
127 elsif ($@) { mumble "error from read: " . $@->errno; last WATCH; }
128 elsif (!$n) { close $sk; $sk = undef; last READ; }
129 else { $buffer .= $b; }
130 }
131
132 @lines = split /\n/, $buffer, -1;
c6377313 133 $buffer = pop(@lines) // "";
8f6b6ec6
MW
134 }
135
136 for my $line (@lines) {
137 my @f = split_fields $line;
138 if ($f[1] eq "state") {
139 if ($f[2] eq "pause") { mumble "paused"; $pause = 1; }
140 elsif ($f[2] eq "resume") { mumble "unpaused"; $pause = 0; }
141 } elsif ($f[1] eq "playing") { mumble "track started"; $track = 1; }
142 elsif ($f[1] eq "completed") { mumble "track finished"; $track = 0; }
143 }
144
145 $NOW = time;
146
147 if ($track && !$pause) { start; } else { pause; }
148 fix_state;
149 }
150}
151
152sub usage (\*) {
153 my ($fh) = @_;
154 print $fh "usage: $PROG [-d] [-u CONFIG] [--] COMMAND ARGS...\n";
155}
156
157sub help () {
158 usage *STDOUT;
159 print <<EOF;
160
161Command-line options:
162 -h, --help Show this help text
163 -u, --user-config Set user configuration file
164EOF
165}
166
167my $bad = 0;
168GetOptions
169 "h|help" => sub { help; exit 0; },
170 "d|debug" => \$DEBUG,
171 "u|user-config=s" => \$CONFFILE
172 or $bad = 1;
173@ARGV > 0 or $bad = 1;
174if ($bad) { usage *STDERR; exit 2; }
175
176for (;;) {
177 eval { watch_status; }; mumble "watcher exited: $@";
178 pause; fix_state;
179 sleep 5;
180}