Skip to content

Commit

Permalink
Check in very rough alpha version of Muse IPC server and ikiwiki test.
Browse files Browse the repository at this point in the history
It won't publish pages or do anything useful yet, other than print
diagnostics messages to stdout.
  • Loading branch information
mwolson committed Aug 5, 2009
1 parent fc01720 commit 0e59623
Show file tree
Hide file tree
Showing 4 changed files with 428 additions and 17 deletions.
206 changes: 206 additions & 0 deletions contrib/ikiwiki/IkiWiki/Plugin/test.pl
@@ -0,0 +1,206 @@

use warnings;
use strict;

use IO::Select qw();
use IO::Socket::INET qw();

my %config = (
muse_emacs => '/usr/local/bin/emacs',
muse_init => '/stuff/proj/personal-site/ikiwiki/muse-init.el',
muse_shared_secret => 'foo',
);

my %MUSE_SERVER = ( host => 'localhost' );

main();
exit 0;

# Determine the emacs binary to use
sub locate_emacs {
my $err = sub {
die "Unable to find your emacs binary.\n",
" Set muse_emacs config to the right value.\n";
};
if ( $config{muse_emacs} ) {
( -x $config{muse_emacs} ) ? return $config{muse_emacs} : $err->();
}
else {
my $emacs = `which emacs`;
chomp $emacs;
( $emacs ) ? return $emacs : $err->();
}
}

# Initialize connection to the Muse IPC server
sub start_muse_server {
my $secret = $config{muse_shared_secret};
my $init_port = $config{muse_init_port} || 0;
my $ipc_port = $config{muse_ipc_port};

# Perform sanity checks
$config{muse_init} or die "Error: muse_init config option not defined.\n";

# Start initialization server
my $pserver = IO::Socket::INET->new(
Proto => 'tcp',
LocalAddr => 'localhost',
LocalPort => $init_port,
Listen => IO::Socket::INET::SOMAXCONN,
) or die "Error: Cannot begin initialization for the Muse IPC server.\n";
$pserver->autoflush(1);
$init_port = $pserver->sockport();
my $select = IO::Select->new($pserver);

# Start Emacs
defined(my $pid = fork()) or die "Error: Unable to fork.\n";
if ( $pid ) {
$MUSE_SERVER{pid} = $pid;
}
else {
exec locate_emacs(),
qw( -q --no-site-file -batch -l ), $config{muse_init},
qw( --eval ), "(muse-ikiwiki-start-server \"$init_port\"" .
( $ipc_port ? " \"$ipc_port\"" : '' ) . ")";
die "Error: Unable to exec emacs.\n";
}

my $emacs_port = undef;

SERVER:
# Respond to clients
while ( my @ready = $select->can_read() ) {
for my $client (@ready) {
if ($client == $pserver) {
my $new = $pserver->accept();
$select->add($new);
next;
}
my $line = <$client>;
chomp $line if defined $line;
if ( defined $line && $line =~ m/^begin (.+)$/s &&
$1 eq $secret ) {
print $client "ok\n";
$line = <$client>;
chomp $line if defined $line;
if ( defined $line && $line =~ m/^port (.+)$/s ) {
$emacs_port = $1;
}
else {
print STDERR <<EOF;
Error: Invalid response while initializing Muse IPC server.
EOF
}
last SERVER;
}
print $client "nok\n" if $line;
$select->remove($client);
$client->close();
}
}
$pserver->close();

if ( $emacs_port ) {
$MUSE_SERVER{port} = $emacs_port;
}
else {
kill_muse_server();
}
}

sub stop_muse_server {
my ( $sock ) = @_;

if ( $MUSE_SERVER{pid} ) {
# Give Muse 3 seconds to stop, presuming that it has already
# been sent the "done" command via stop_muse_server.
local $SIG{ALRM} = sub {
kill 9, $MUSE_SERVER{pid};
die "Timeout";
};
eval {
alarm 3;
print $sock "done\n";
$sock->close();
waitpid($MUSE_SERVER{pid}, 0);
alarm 0;
};
delete $MUSE_SERVER{pid};
}
else {
print $sock "done\n";
$sock->close();
}
}

sub kill_muse_server {
my @msgs = @_;

kill 9, $MUSE_SERVER{pid} if $MUSE_SERVER{pid};
die @msgs if @msgs;
}

sub ipc_expect_ok {
my ( $sock, $err_msg ) = @_;
$err_msg = "Error: Command did not succeed on Muse IPC server.\n";

my $line = <$sock>;
chomp $line;
if ( $line ne 'ok' ) {
$sock->close();
kill_muse_server $err_msg;
}
}

sub ipc_connect {
my $secret = $config{muse_shared_secret};
my $host = $MUSE_SERVER{host};
my $port = $MUSE_SERVER{port};
$host && $port
or kill_muse_server "Error: No Muse IPC server is active.\n";

# Start client connection
my $sock = IO::Socket::INET->new(
Proto => 'tcp',
PeerAddr => $host,
PeerPort => $port,
) or kill_muse_server "Error: Cannot connect to the Muse IPC server.\n";
$sock->autoflush(1);

# Authenticate
print $sock "begin $secret\n";
ipc_expect_ok $sock,
"Error: Could not authenticate to the Muse IPC server.\n";

return $sock;
}

sub test_name {
my ( $sock ) = @_;

print $sock "name foobar\n";
ipc_expect_ok $sock,
"Error: Could not set name of page on Muse IPC server.\n";
}

sub test_title {
my ( $sock ) = @_;

print $sock "title quux\n";
ipc_expect_ok $sock,
"Error: Could not set title of page on Muse IPC server.\n";
}

sub main {
print "Starting Muse server ...\n";
start_muse_server();

print "Got port $MUSE_SERVER{port}.\n";
my $sock = ipc_connect();
test_name($sock);
test_title($sock);

print "Shutting down ...\n";
stop_muse_server($sock);
print "Done shutting down.\n";
}
41 changes: 27 additions & 14 deletions lisp/muse-ikiwiki.el
Expand Up @@ -83,38 +83,51 @@ For more on the structure of this list, see `muse-publish-markup-regexps'."
'(muse-no-paragraph t)) '(muse-no-paragraph t))
(muse-publish-mark-read-only (match-beginning 0) (match-end 0)))) (muse-publish-mark-read-only (match-beginning 0) (match-end 0))))


(defun muse-ikiwiki-publish-buffer (name title &optional style)
"Publish a buffer for Ikiwki.
The name of the corresponding file is NAME.
The name of the style is given by STYLE. It defaults to \"ikiwiki\"."
(unless style (setq style "ikiwiki"))
(unless title (setq title (muse-page-name name)))
(let ((muse-batch-publishing-p t)
(muse-publishing-current-file name)
(muse-publishing-current-output-path file)
(muse-publishing-current-style style)
(font-lock-verbose nil)
(vc-handled-backends nil)) ; don't activate VC when publishing files
(run-hooks 'muse-before-publish-hook)
(let ((muse-inhibit-before-publish-hook t))
(muse-publish-markup-buffer title style))))

(defun muse-ikiwiki-publish-file (file name &optional style) (defun muse-ikiwiki-publish-file (file name &optional style)
"Publish a single file for Ikiwiki. "Publish a single file for Ikiwiki.
The name of the style is given by STYLE. It defaults to \"ikiwiki\".
The name of the real file is NAME, and the name of the temporary The name of the real file is NAME, and the name of the temporary
file containing the content is FILE." file containing the content is FILE.
The name of the style is given by STYLE. It defaults to \"ikiwiki\"."
(if (not (stringp file)) (if (not (stringp file))
(message "Error: No file given to publish") (message "Error: No file given to publish")
(unless style (unless style
(setq style "ikiwiki")) (setq style "ikiwiki"))
(let ((muse-batch-publishing-p t) (let ((output-path file)
(title (muse-page-name name))
(output-path file)
(target file) (target file)
(muse-publishing-current-file file) (vc-handled-backends nil) ; don't activate VC when publishing files
(muse-publishing-current-output-path file) auto-mode-alist
(font-lock-verbose nil)
muse-current-output-style) muse-current-output-style)
;; don't activate VC when publishing files
(setq vc-handled-backends nil)
(setq muse-current-output-style (list :base style :path file))
(setq auto-mode-alist (setq auto-mode-alist
(delete (cons (concat "\\." muse-file-extension "\\'") (delete (cons (concat "\\." muse-file-extension "\\'")
'muse-mode-choose-mode) 'muse-mode-choose-mode)
auto-mode-alist)) auto-mode-alist))
(setq muse-current-output-style (list :base style :path file))
(muse-with-temp-buffer (muse-with-temp-buffer
(muse-insert-file-contents file) (muse-insert-file-contents file)
(run-hooks 'muse-before-publish-hook) (muse-ikiwiki-publish-buffer name nil nil style)
(let ((muse-inhibit-before-publish-hook t))
(muse-publish-markup-buffer title style))
(when (muse-write-file output-path t) (when (muse-write-file output-path t)
(muse-style-run-hooks :final style file output-path target)))))) (muse-style-run-hooks :final style file output-path target))))))


(defun muse-ikiwiki-start-server (port)
"Start Muse IPC server, initializing with the client on PORT."
(muse-ipc-start "foo" #'muse-ikiwiki-publish-buffer port))

;;; Colors ;;; Colors


(defface muse-ikiwiki-directive (defface muse-ikiwiki-directive
Expand Down

0 comments on commit 0e59623

Please sign in to comment.