<?xml version="1.0"?>
<!DOCTYPE article
          PUBLIC "-Norman Walsh//DTD Simplified DocBk XML V3.1.7.1//EN"
          "sdocbook.dtd">
<article>
<artheader>
<author>
<firstname>Brian</firstname>
<surname>McNett</surname>
</author>
<date>Feb 4, 2000</date>
<pubdate>Feb 4, 2000</pubdate>
<issuenum>10</issuenum>
<title>Mac Daemons in Perl</title>
<productname>perl</productname>
<keywordset>
<keyword>daemon</keyword>
</keywordset>
</artheader>

<abstract>
<para>On the Mac, under MacPerl, one can't fork a process like Unix, and 
there's no "root" to place such a process to begin with. Nevertheless,
Brian McNett was able to put together a Mac daemon equivalent with a little
help from Chris Nandor.</para>
</abstract>

<sect1>
<title>Introduction</title>
<sect2>
<title>Abstract</title>
<para>On the Mac, under MacPerl (as it's currently written), one can't fork a 
process, and there's no <emphasis>root</emphasis>
 to place such a process to begin with. 
Thus, unless you want to patch the operating system at startup, your 
process has to live in the application heap with all the other 
user processes.  A <emphasis>true</emphasis> unix-like daemon is 
not an option (alas) in MacPerl. 
It is possible, however, to create a runtime application, which is 
basically a cut-down MacPerl app with just the interpreter and a copy of 
your code, (and sometimes even all the modules it uses as well, but 
that's an issue for another day) which it executes.</para>
</sect2>

<sect2>
<title>Required Modules</title>
<para>The heavy lifting in the program is done by three modules: 
<function>Mac::Processes</function>, <function>Mac::Apps::Launch</function>,
and <function>Mac::Speech</function>. <function>Processes.pm</function>
and <function>Speech.pm</function> are interfaces to the Mac Toolbox APIs 
for the Process Manager and Speech manager. As such, the details involving 
their use are found in Apple's extensive technical tome 
<emphasis>Inside Macintosh</emphasis>.  
I've written a quick tutorial on using Mac::Speech, which appeared in 
issue #6 of <ulink href="http://www.perlmonth.com/">PerlMonth</ulink>.  
I recommend it, as it's a fun read.</para>
</sect2>
</sect1>

<sect1>
<title>The Code</title>
<para>
<programlisting><![CDATA[
Hide('MacPerl', 1) or warn $^E;
my $channel = NewSpeechChannel($Voice{Victoria});
]]></programlisting></para>

<para>After loading in our modules, we immediately invoke 
<function>Mac::Apps:Launch::Hide()</function>, 
and tell it to force MacPerl into the 
background.  Next, we create a new speech channel using 
<function>Mac::Speech::NewSpeechChannel()</function>.  
I'm not going into detail about what 
arguments <function>NewSpeechChannel()</function> takes, 
as I did that in my PerlMonth article.  Still it should be clear 
that there is a hash <varname>Mac::Speech::%Voice</varname>, 
and I've passed a member of that hash 
<varname>$Voice{Victoria}</varname> 
to <function>NewSpeechChannel()</function>, and assigned the result to 
<varname>$channel</varname>.</para>

<para><programlisting>
    for (;;) {
</programlisting></para>

<para>Next we loop <emphasis>forever</emphasis>, or at least until 
someone brings the application to the front and aborts it by typing 
<command>Cmd-.</command> (Cmd is that funky clover-leaf key, and Cmd-. 
is pretty much standard for aborting a running process).

<programlisting><![CDATA[
        my ( $year, $month, $day, $hour, $min, $sec) = 
reverse((localtime)[0 .. 5]);
        printf('%04d:%02d:%02d 
%02d:%02d:%02d',$year+1900,$month+1,$day,$hour,$min,$sec);
        print "\n------------------------------\n";
        foreach my $psi (values %Process) {
       	printf("%s %s [%s bytes]\n",
       	   $psi->processSignature(),
       	   $psi->processName(),
           $psi->processSize()
           );
]]></programlisting></para>

<para>This program doesn't log its output to a file (I could have done that, 
though), but just prints it to the MacPerl console.  Each iteration gets 
a timestamp.  Then for each member of the hash 
<varname>%Process</varname> (which is a hash 
of hashes, containing the <constant>_processInfoMap</constant>
 for each currently running 
process), we extract the application <emphasis>signature</emphasis> 
which is a unique four 
character code used to identify each application: <emphasis>MACS</emphasis>
 for the Finder, 
<emphasis>R*ch</emphasis> for BBEdit, and so forth.  We then do the same 
for the application <emphasis>name</emphasis> (which is just what it says it 
is).  Finally, we get the application 
size in bytes.  Once again, I'm not giving ALL the details (a reading of 
Inside Macintosh is REALLY required!).

<programlisting><![CDATA[
           my $process_name = $psi->processName();
       }]]></programlisting></para>

<para>In the previous bit of code, <function>printf()</function> 
handled the appropriate coercion 
for us.  It's implicit when we say <function>printf("%s")</function>
 that we want the output 
coerced to a string.  In the next step, however, we have to do the 
coercion ourselves.  <function>SpeakText()</function> expects a string as 
its argument, so we assign <function>$psi->processName()</function>
 to <varname>$process_name</varname>.

<programlisting><![CDATA[
           SpeakText($channel, "$process_name" );
                while (SpeechBusy()) {}
]]></programlisting></para>

<para>Whenever you make a call to <function>SpeakText()</function>, 
it needs to be followed by a call to 
<function>while( SpeechBusy()){}</function>.  You could actually do 
something inside that loop while waiting for the speech to end, but 
I've chosen not to (keeping things simple).

<programlisting>
    sleep(15);
</programlisting></para>

<para>Finally, we put the program to sleep for 15 seconds.  During that time, 
MacPerl will indicate to the OS that it's not doing anything, and the OS 
will make that unused time available to the other processes.

One last detail!  If somehow, the code breaks out of its infinite loop 
(say someone does invoke <command>Cmd-.</command>), we need to dispose 
of the open speech channel.

<programlisting>
    DisposeSpeechChannel($channel);
</programlisting></para>
</sect1>

<sect1>
<title>In Conclusion</title>
<sect2>
<title>The Future</title>
<para>
Matthias Neeracher is busily re-writing MacPerl to take advantage of 
GUSI2.  GUSI is Matthias' 
<citation>Generic Universal Socket Interface</citation>,  
essentially a network socket and POSIX library for the Mac.  GUSI2 adds 
pthreads, and a host of other features which will allow Matthias to 
finally bring MacPerl into sync with the standard distribution.  No word 
on how far he is from completion.
Eventually, the <emphasis>Mac</emphasis> in MacPerl will devolve 
into an extended set of 
modules for the Mac Toolbox and Graphics APIs which will run directly 
over the standard Perl.  The above code will likely survive that change, 
but it will then be possible to write a normal <emphasis>daemon</emphasis>
 process on the Mac.</para>
</sect2>

<sect2>
<title>About the Author</title>
<para>Brian McNett is Webmaster of 
<ulink href="http://www.mycoinfo.com/">Mycoinfo</ulink>, the world's first 
mycology e-journal. He's also a regular contributor to 
<ulink href="http://www.motherofperl.com">Mother of Perl</ulink>
where he keeps things <emphasis>Mac friendly</emphasis>.
Please send comments, praises, and sizable cash contributions to
<ulink href="mailto:webmaster@mycoinfo.com">webmaster@mycoinfo.com</ulink></para>
</sect2>

<sect2>
<title>Source Code</title>
<para>The full source code to the MacPerl daemon is available at:
<ulink href="http://www.motherofperl.com/tutorial/10/macdaemon.pl">http://www.motherofperl.com/tutorial/10/macdaemon.pl</ulink></para>
</sect2>


</sect1>
</article>














