mirror of
https://github.com/brmlab/brmdoor.git
synced 2025-06-08 11:44:01 +02:00
225 lines
10 KiB
Perl
225 lines
10 KiB
Perl
# fifo_remote.pl -- send commands to Irssi through named pipe/fifo
|
||
#
|
||
# DESCRIPTION
|
||
#
|
||
# fifo_remote.pl creates a named pipe (a.k.a. fifo). Everything written to the
|
||
# fifo will be passed on to Irssi and run as a command. (You need not precede
|
||
# you commands with `/', or whatever you've set you `cmdchars' to -- this also
|
||
# means that if you want to send a message, you must use the explicit `say'
|
||
# command.)
|
||
#
|
||
# SETTINGS
|
||
#
|
||
# fifo_remote_file (default is `remote-control')
|
||
#
|
||
# This is the file name of the named pipe. Any leading `~[USER]' part is
|
||
# expanded before use. If the name does not begin with `/' it is taken to
|
||
# be relative to Irssi's current configuration dir (usually `~/.irssi').
|
||
#
|
||
# The default value thus normally means `~/.irssi/remote-control'.
|
||
#
|
||
# NOTES
|
||
#
|
||
# This script may have limited use to you, since it cannot bring back *any*
|
||
# return values from Irssi. It can only run Irssi commands from the outside. --
|
||
# I use it to trigger my online/away messages from any shell, and that's about
|
||
# it.
|
||
#
|
||
# CAVEATS
|
||
#
|
||
# Due to the way named pipes (or fifos) works one must take extra care when
|
||
# writing to one of these beasts -- if not, the writing program may well hang
|
||
# forever. This is because the writing process will not terminate until the
|
||
# fifo has been read from the other end (see also fifo(4)).
|
||
#
|
||
# To avoid this problem, I usually use something akin to the below shell
|
||
# function to write to the Irssi fifo remote. It simply kills off the writing
|
||
# process if it's still around after a certain timeout (e.g. the fifo could be
|
||
# present but Irssi not running -- and thus the pipe would never be read). The
|
||
# entire process is done in the background, so the caller of the function does
|
||
# not have to wait.
|
||
#
|
||
# FILE="$HOME/.irssi/remote-control"
|
||
# irssi_command() {
|
||
# if [ -p "$FILE" ]; then
|
||
# ( echo "$*" > "$FILE" &
|
||
# sleep 5
|
||
# kill $! 2>/dev/null )&
|
||
# fi
|
||
# }
|
||
#
|
||
# TODO
|
||
#
|
||
# o Clean up fifo file when Irssi quits -- right now this is not done, so extra
|
||
# precautions are required inside your shell scripts to make sure they do not
|
||
# hang indefinately when trying to write to the remote control fifo. (See
|
||
# above example.)
|
||
#
|
||
# HISTORY
|
||
#
|
||
# [2004-08-12, 22.16-00.58] v0.1a - began implementing it
|
||
#
|
||
# [2004-08-13, 09.52-10-19] v0.2a - began implementing fifo_read
|
||
#
|
||
# [2004-08-14, 01.12-04.27] v0.3a
|
||
#
|
||
# [2004-08-14, 14.09-18.13] v0.4a - seems to be fully functional, except for
|
||
# the fact that commands aren't run in the proper window/server environment
|
||
#
|
||
# [2004-08-15, 18.17-19.26] v0.5a - command comming through pipe is now run in
|
||
# the active window; removed bug which crashed Irssi, bug was caused by several
|
||
# input_add()s being called without having been removed in between
|
||
#
|
||
# [2004-08-26, 21.46-22.30] v0.5 - wrote above docs
|
||
#
|
||
|
||
our $VERSION = '0.5';
|
||
our %IRSSI = (
|
||
authors => 'Zrajm C Akfohg',
|
||
contact => 'zrajm\@klingonska.org',
|
||
name => 'fifo_remote',
|
||
description => 'Irssi remote control (for shell scripting etc.) -- ' .
|
||
'run all commands written to named pipe.',
|
||
license => 'GPLv2',
|
||
url => 'http://www.irssi.org/scripts/',
|
||
); #
|
||
use strict; #
|
||
use Irssi; #
|
||
use Fcntl; # provides `O_NONBLOCK' and `O_RDONLY' constants
|
||
our ( $FIFO, # fifo absolute filename (expanded from Irssi config)
|
||
$FIFO_HANDLE, # fifo filehandle for `open' et al.
|
||
$FIFO_TAG ); # fifo signal tag for `input_add'
|
||
|
||
# simple subs
|
||
sub TRUE() { 1 } # some constants [perlsyn(1)
|
||
sub FALSE() { "" } # "Constant Functions"]
|
||
sub DEBUG(@) { print "%B", join(":", @_),"%n" }# DEBUG thingy
|
||
|
||
|
||
# Irssi settings
|
||
Irssi::settings_add_str($IRSSI{name}, # default fifo_remote_file
|
||
'fifo_remote_file', 'remote-control'); #
|
||
|
||
|
||
# create named fifo and open it for input
|
||
# (called on script load and fifo name changes)
|
||
sub create_fifo($) { # [2004-08-14]
|
||
my ($new_fifo) = @_; # get args
|
||
if (not -p $new_fifo) { # create fifo if non-existant
|
||
if (system "mkfifo '$new_fifo' &>/dev/null" and
|
||
system "mknod '$new_fifo' &>/dev/null"){
|
||
print CLIENTERROR "`mkfifo' failed -- could not create named pipe";
|
||
# TODO: capture `mkfifo's stderr and show that here
|
||
return ""; #
|
||
} #
|
||
} #
|
||
$FIFO = $new_fifo; # remember fifo name
|
||
open_fifo($new_fifo); # open fifo for reading
|
||
} #
|
||
|
||
|
||
# set up signal to trigger on fifo input
|
||
# (called when creating named fifo)
|
||
sub open_fifo($) { # [2004-08-14]
|
||
my ($fifo) = @_; # get args
|
||
if (not sysopen $FIFO_HANDLE, $fifo, # open fifo for non-blocking
|
||
O_NONBLOCK | O_RDONLY) { # reading
|
||
print CLIENTERROR "could not open named pipe for reading";
|
||
return ""; #
|
||
} #
|
||
Irssi::input_remove($FIFO_TAG) # disable fifo reading signal
|
||
if defined $FIFO_TAG; # if there is one
|
||
$FIFO_TAG = Irssi::input_add # set up signal called when
|
||
fileno($FIFO_HANDLE), INPUT_READ, # there's input in the pipe
|
||
\&read_fifo, ''; #
|
||
return 1; #
|
||
} #
|
||
|
||
|
||
# read from fifo
|
||
# (called by fifo input signal)
|
||
sub read_fifo() { # [2004-08-14]
|
||
foreach (<$FIFO_HANDLE>) { # for each input line
|
||
chomp; # strip trailing newline
|
||
Irssi::print( #
|
||
"%B>>%n $IRSSI{name} received command: \"$_\"",
|
||
MSGLEVEL_CLIENTCRAP); #
|
||
# Irssi::active_win->print( # show incoming commands
|
||
# "\u$IRSSI{name} received command: \"$_\"", #
|
||
# MSGLEVEL_CLIENTNOTICE); #
|
||
Irssi::active_win->command($_); # run incoming commands
|
||
} #
|
||
open_fifo($FIFO); # re-open fifo
|
||
# TODO: Is the above re-opening of fifo really necessary? -- If not
|
||
# invoked here `read_fifo' is called repeatedly, even though no input
|
||
# is to be found on the fifo. (This seems a waste of resources to me.)
|
||
} #
|
||
|
||
|
||
# disable fifo and erase fifo file
|
||
sub destroy_fifo($) { # [2004-08-14]
|
||
my ($fifo) = @_; # get args
|
||
if (defined $FIFO_TAG) { # if fifo signal is active
|
||
Irssi::input_remove($FIFO_TAG); # disable fifo signal
|
||
undef $FIFO_TAG; # and forget its tag
|
||
} #
|
||
if (defined $FIFO_HANDLE) { # if fifo is open
|
||
close $FIFO_HANDLE; # close it
|
||
undef $FIFO_HANDLE; # and forget handle
|
||
} #
|
||
if (-p $fifo) { # if named fifo exists
|
||
unlink $fifo; # erase fifo file
|
||
undef $FIFO; # and forget filename
|
||
} #
|
||
return 1; # return
|
||
} #
|
||
|
||
|
||
# add path to filename (expands `~user', and make
|
||
# non-absolute filename relative to Irssi's config dir)
|
||
sub absolute_path($) { # [2004-08-14] -- [2004-08-15]
|
||
my ($file) = @_; #
|
||
return '' if $file eq ''; # don't modify empty value
|
||
$file =~ s<EFBFBD>^(~[^/]*)<EFBFBD> # expand any leading tilde
|
||
my $x; # WORKAROUND: glob()
|
||
until($x = glob($1)) { }; # sometimes return empty
|
||
$x; # string -- avoid that
|
||
<EFBFBD>ex; #
|
||
$file = Irssi::get_irssi_dir() . "/$file" # if full path is not given
|
||
unless $file =~ m<EFBFBD>^/<EFBFBD>; # prepend irssi config path
|
||
# FIXME: clean up multiple slashes, and occuring `/./' and `/../'.
|
||
# this sub used in: fifo_remote.pl, xcuses.pl
|
||
return $file; #
|
||
} #
|
||
|
||
|
||
# clean up fifo on unload
|
||
# (called on /script unload)
|
||
Irssi::signal_add_first #
|
||
'command script unload', sub { # [2004-08-13]
|
||
my ($script) = @_; # get args
|
||
return unless $script =~ # only do cleanup when
|
||
/(?:^|\s) $IRSSI{name} # unloading *this* script
|
||
(?:\.[^. ]*)? (?:\s|$) /x; #
|
||
destroy_fifo($FIFO) if -p $FIFO; # destroy old fifo
|
||
Irssi::print("%B>>%n $IRSSI{name} $VERSION unloaded", MSGLEVEL_CLIENTCRAP);
|
||
}; #
|
||
|
||
|
||
# create new fifo (erase any old) and get command prefix
|
||
# (called on script loading and on user /set)
|
||
sub setup() { # [2004-08-13]
|
||
my $new_fifo = absolute_path # get fifo_remote_file
|
||
Irssi::settings_get_str # setting from Irssi
|
||
'fifo_remote_file'; # (and add path to it)
|
||
return if $new_fifo eq $FIFO and -p $FIFO; # do nada if already exists
|
||
destroy_fifo($FIFO) if -p $FIFO; # destroy old fifo
|
||
create_fifo($new_fifo) # create new fifo
|
||
and $FIFO = $new_fifo; # and remember that fifo
|
||
} #
|
||
|
||
setup(); # initialize setup values
|
||
Irssi::signal_add('setup changed', \&setup); # re-read setup when it changes
|
||
print CLIENTCRAP "%B>>%n $IRSSI{name} $VERSION (by $IRSSI{authors}) loaded";
|
||
#print CLIENTCRAP " (Fifo name: $FIFO)";
|
||
|