On Mar 28, 2009, at 8:27 AM, Peter Hosey wrote:

>
> On Mar 26, 2009, at 11:58:58, Richard L. Hamilton wrote:
>> On Mar 26, 2009, at 5:02 AM, Peter Hosey wrote:
>>> Perhaps if you intend to release your notifier to the public, you
>>> may want to make the port configurable. Or use Bonjour.
>>
>> I could do the former, although not the latter without modifying
>> your code, …
>
> Why? It's just a user default, so you could make a small tool, perhaps
> using Platypus, to allow users or admins to easily change the port
> Growl listens on.

I don't know if prefs in ~/Library/Preferences, /Library/Preferences,
/Network/Library/Preferences (if present), and /System/Library/ 
Preferences
are merged, or if the first applicable plist in that order is used in  
its
entirety.  In any case, I see my personal preferences have the UDP  
port number
in them, and I never explicitly set that, so it would involve changing
any existing users and/or
/Library/PreferencePanes/Growl.prefPane/Contents/Resources/ 
GrowlHelperApp.app/Contents/Resources/GrowlDefaults.plist
which I assume is the initial defaults given to user preferences.

>
> Ideally, your rpc.walld could even look in each user's preferences to
> find the correct port to use to connect to their Growl.

Well, first, I think I'll modify the Darwin version of "wall" (which
rpc.rwalld uses) to add Growl functionality, rather than modifying  
rpc.rwalld.
That makes more sense, and is more generally useful, too.  Second, a
system daemon that goes around looking at individual user preference  
files
is usually _not_ a good idea, since it would have to employ root  
privileges
(or some subset thereof sufficient to override permission checks, if  
possible).
So as far as a modified "wall" is concerned, I think I'd have it act  
as if
everyone on the system would be using the same port, and merely provide
a single file it can look at to see if something other than the default
applies.  Since "wall" is pure C, looking in GrowlDefaults.plist (or  
wherever
Growl keeps its initial per-user defaults) would mean rolling my own  
code
for parsing either XML or binary plists, which is _way_ more bother than
I'm looking for.  Unless one can call Objective C from C...I don't  
know...

>
>> … since the page said the UDP doesn't use Bonjour; …
>
> Oops. Right. Sorry.
>
>> … I gather the more sophisticated TCP protocol would take me out of
>> the realm of pure C, …
>
> Right: It's Cocoa Distributed Objects. We're working on a new  
> platform-
> agnostic protocol, primarily for use over TCP. We don't know when it
> will make it into a Growl release.
>
>> Also, a per-system process probably isn't going to want to deal with
>> per-user preferences, _except_ via Bonjour.  For a workstation, it
>> might just barely be possible to figure out somehow who the
>> _current_ GUI session user is, but that would require excessive
>> privileges, and there's a scenario (below) that still presents
>> problems.
>>
>>>> * is growlnotify reliable enough that I could just posix_spawn()
>>>> it, which would avoid all the issues with a network connection?
>>>
>>> When using the UDP protocol, yes it is. However, only one user's
>>> GrowlHelperApp can bind a socket on our UDP port at a time, which
>>> means only one user per machine will see your notifications.
>> [...]
>>
>> … on a workstation, AFAIK only one console user can actually be
>> _active_ at a time, although others could well have processes
>> running given fast user switching.
>
> Right. That would mitigate the problem, except that Growl binds the
> socket only on launch, not on session-resume. That means that (in my
> testing) the last Growl to bind wins, and all the others come unbound
> and won't receive network notifications, even when the user who owns
> one of those processes resumes his session.
>
>> Presumably most Growl notifications are delivered via some local
>> communications channel, …
>
> Cocoa Distributed Objects (which work locally as well as over a
> network).
>
>> … which _is_ per user, …
>
> The documentation for NSConnection suggests that it's per-host, but
> that seems counter-intuitive for local connections, and I seem to
> remember having multiple Growls running and working with local
> notifications.
>
>> The only problem I see is that with fast user switching, if a user
>> other than the current one could be bound to the port, then the
>> current user would never
>> see the message.
>
> Exactly.
>
>> But on a Mac _server_, I believe there are one or more commercial
>> terminal server software packages available, that act much as WTS or
>> Citrix do for Windows, allowing multiple simultaneous graphical
>> sessions to be accessed via some remote display mechanism…
>
> Yeah. One way or another, we should definitely do the right thing for
> concurrent logged-in users at some point.
>
>> I gather it is sort of possible, if tedious, to have system and user
>> processes communicate, if not readily with Mach ports, then perhaps
>> with Unix Domain sockets or named pipes or some such.
>
> Pipes are one-way, and Growl notifications can be two-way sessions (if

Ok, now I know I've gotten spoiled, which is why I hadn't even though  
to check
the OS X pipe(2) man page.  Pipes only _have_ to be unidirectional,
but most other BSD derived systems implement pipes as a pair of
nameless local sockets, much like socketpair(AF_UNIX,SOCK_STREAM,0,fds).
Likewise most systems with STREAMS being more basic than sockets  
implement
pipes as STREAMS pipes (a bidirectional connection between two STREAMS  
heads)
and even able to have STREAMS modules pushed onto them.  I think the  
last
system I saw with unidirectional pipes was running SVR2 or the like.   
The
nice thing on systems that provide bidirectional named pipes is you  
create
them with mkfifo() not socket(), and you can just open() them rather  
than
create a nameless socket and bind it, so a named pipe can be kept around
rather than created, used, and unlinked.  And permissions and fetching
peer creds, and even handing off file descriptors through it work  
(although
the details of the latter differ between socket and STREAMS based
implementations, the STREAMS way of course being more of a pain).  I  
wonder
why Apple went with such a lame implementation of pipes?  I suppose  
I'd have
have to look at the XNU code (which I'm already doing for other reasons)
to figure that out.


> Yup. A daemon to handle network notifications, and one agent
> (GrowlHelperApp) per user session to handle local notifications. The
> daemon would forward network notifications locally to the agent.
>
> Further reading: http://developer.apple.com/technotes/tn2005/tn2083.html

Yes, I saw that once trying to understand some of the Mac peculiarities
(relative to more traditional Unix implementations), some of which  
having
to do with launchd replacing init and partially (doesn't appear to be  
able
to set up listening for RPC services) replacing inetd; and also the  
oddities
of the use of Mach ports, with their system as well as per-user  
namespace.
I still don't quite understand the various Mach services, but I  
haven't ever
worked on a Mach-based system before.  No doubt more reading of kernel  
source,
and that book on Mac OS internals, will help there.


> (GrowlHelperApp isn't a *launchd* agent, and I haven't read enough of
> that technote yet to know whether it'd be worth changing it over to be
> one. I doubt it.)

I think programs that do _not_ run on-demand can be started as launchd
daemons or agents (as appropriate) with little or no modification; there
merely must not attempt to daemonize themselves.  I'm starting my  
rpc.rwalld
via a plist in /Library/LaunchDaemons, and it seems to work ok,  
although I
haven't figured out yet how to make sure that it starts after  
portmapper.
They say that launchd doesn't handle dependencies as such, and you're  
supposed
to use IPC to take care of that, but I haven't seen much in the way of
examples yet.  But I've just started looking at Darwin code.
>

> One thing we'd have to be careful about is making sure that the daemon
> handles dispatching the notifications to the users' GHAs based on the
> users' hostname whitelists. Otherwise, every user would see every
> incoming network notification—bad for privacy.

I think once again, the daemon should know as little as possible about
the user agents or preferences as possible, which means the protocol
between the daemon and the GHA would have to pass along the host of  
origin.
That would have the advantage that system policy could set the outer  
limits
of what could pass, and users could further restrict as they wished.

>
>> Next, I don't pretend to understand either Objective C or Apple's
>> CoreFoundation framework, but it appears to me from an incredibly
>> cursory look at the code that you can tell the IP address of where a
>> network notification came from.
>
> I believe that's true of sockets in general, although I'm no expert in
> that API.

In general yes.  But with the UDP (where each packet may come from a
separate host), you already get that information for free, whereas with
TCP, I suspect you have to go to a little extra trouble to get it;  
again,
not unlike BSD sockets without the CoreFoundation API.

BTW, I talked to the person with the C binding code; he doesn't want to
change the license on the copies on his web site, but sent me a copy
of the two files implementing it under LGPLv2 rather than GPLv2, which
should cause less problems, since that would allow resdistributing  
binaries
composed of linked LGPLv2 and BSD code along with _only_ the source  
for the
LGPLv2 components (if that's what one wished).  Since I gather some  
Growl
supporting apps are or may be proprietary, that seemed necessary.  I  
tried
to suggest that existing Growl being under BSD, that might be the least
problematic, but not wanting to push too hard, figured that LGPLv2 was  
better
for these purposes than GPL, which might exclude some potential  
proprietary
apps entirely.

I can simply pass those on if anyone wants.  But they're pretty  
specific to
what he was doing as they are now; for instance, the priority/stickiness
were hard-coded; I'd like to make them parameters.  I need to understand
better what it means for an app to register with multiple  
notifications too,
since his code only registered one notification.  He did have separate
register and notification functions, which is important.  I wanted his  
code
as a starting point because the protocol description rules out easier
solutions like structs (because of the varying length parts) or XDR
(because of how the varying length parts are represented, as well as  
because
of how XDR represents ints, where as I understand it all are not less  
than
4 bytes over the wire, even if they're a smaller size; XDR mainly having
been designed to build CPU-efficient RPC code on top of).  So he does  
what the
only other thing I can think of is, namely builds it in a buffer one  
field
at a time, via a pointer.  Given that there doesn't appear to be a  
whole lot
of better choices (in a general sense) of how to build the packet, I  
thought
his code was as good a starting point for me (or, once parameterized a  
bit more,
anyone else wanting to do something similar) as anything else, and I'd  
much
rather use that sort of technique starting from something that works  
than
starting from scratch.

It'll be awhile before I find time to learn what I need to learn and get
them parameterized to a point where they provide easy access to most of
the functionality of the protocol, since I may have various things  
keeping
me moderately busy for at least a month or so (paperwork, Easter, etc).


--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Growl Discuss" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to 
[email protected]
For more options, visit this group at 
http://groups.google.com/group/growldiscuss?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to