Permalink
Browse files

Check in very rough alpha version of Muse IPC server and ikiwiki test.

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 0e59623c3727954b9b2245ff5df632334a95ee61
Showing with 428 additions and 17 deletions.
  1. +206 −0 contrib/ikiwiki/IkiWiki/Plugin/test.pl
  2. +27 −14 lisp/muse-ikiwiki.el
  3. +194 −0 lisp/muse-ipc.el
  4. +1 −3 lisp/muse-publish.el
@@ -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";
+}
View
@@ -83,38 +83,51 @@ For more on the structure of this list, see `muse-publish-markup-regexps'."
'(muse-no-paragraph t))
(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)
"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
-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))
(message "Error: No file given to publish")
(unless style
(setq style "ikiwiki"))
- (let ((muse-batch-publishing-p t)
- (title (muse-page-name name))
- (output-path file)
+ (let ((output-path file)
(target file)
- (muse-publishing-current-file file)
- (muse-publishing-current-output-path file)
- (font-lock-verbose nil)
+ (vc-handled-backends nil) ; don't activate VC when publishing files
+ auto-mode-alist
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
(delete (cons (concat "\\." muse-file-extension "\\'")
'muse-mode-choose-mode)
auto-mode-alist))
+ (setq muse-current-output-style (list :base style :path file))
(muse-with-temp-buffer
(muse-insert-file-contents file)
- (run-hooks 'muse-before-publish-hook)
- (let ((muse-inhibit-before-publish-hook t))
- (muse-publish-markup-buffer title style))
+ (muse-ikiwiki-publish-buffer name nil nil style)
(when (muse-write-file output-path t)
(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
(defface muse-ikiwiki-directive
Oops, something went wrong.

0 comments on commit 0e59623

Please sign in to comment.