You can do this with our UDP protocol:
http://growl.info/documentation/developer/protocol.php
There are a few other programs that use it on other platforms,
including notification systems (like Growl) for Windows and GNOME.
Yes.
> * The page above mentions UDP port 9887, but says "The UDP protocol
> is not advertised because its port number is configurable by the
> user." However, while the preference pane certainly _tells_ me the
> TCP and UDP port numbers, I don't see any option to configure them.
> Are they only configurable by altering the source code?
No; you can use Mac OS X's user defaults system to set the port. That
requires a command such as:
defaults write com.growl.growlhelperapp GrowlUDPPort -int 42000
However, unless your users are comfortable in the terminal, feel like
diving into our source code to learn how to change the port, and even
know that it's changeable, I wouldn't worry about it.
Perhaps if you intend to release your notifier to the public, you may
want to make the port configurable. Or use Bonjour.
> * 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.
> Would growlnotify work when run from something not in a user session
> context?
When using the UDP protocol, yes, because the UDP socket is not per-
session.
However, GrowlHelperApp itself *is* per-session (since it displays
notifications using windows in the user's session). You need a way to
communicate from the single rpc.walld to all of the GrowlHelperApps
that are running—one per logged-in user. Using UDP will only reach one
of them.
I'm actually not sure what would work, except to set each user's Growl
to listen on a different port and to make your rpc.walld send a
notification to every port you've set. To be honest, I don't think any
of us have ever had to solve the problem of multiple Growl processes
running on a single machine before.
Longer-term, it would probably be a good idea for us to create a
daemon that can listen for network notifications, and forward them to
every user's Growl process. The Growl processes, for their part, would
have to check in with this daemon to request a port number to
(locally) bind. Patches welcome.
>
> On Mar 25, 2009, at 07:27:13, rlhamil wrote:
[...]
>> * The page above mentions UDP port 9887, but says "The UDP protocol
>> is not advertised because its port number is configurable by the
>> user." However, while the preference pane certainly _tells_ me the
>> TCP and UDP port numbers, I don't see any option to configure them.
>> Are they only configurable by altering the source code?
>
> No; you can use Mac OS X's user defaults system to set the port. That
> requires a command such as:
>
> defaults write com.growl.growlhelperapp GrowlUDPPort -int 42000
>
> However, unless your users are comfortable in the terminal, feel like
> diving into our source code to learn how to change the port, and even
> know that it's changeable, I wouldn't worry about it.
>
> 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,
since the page said the UDP doesn't use Bonjour; I gather the more
sophisticated
TCP protocol would take me out of the realm of pure C, and even if I
could use
Bonjour with the UDP protocol, that adds more complications than I'd
care
for if trying to do minimal mods to existing C apps. 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, issues involving multiple console users shouldn't
matter, at least not not for what I'm doing now, in the context of
most "wall" messages being issued automatically by (non-Apple) NFS
servers to their clients when the server goes down (since client
accesses to NFS hard-mounted files will simply hang until
the server comes back up). And 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. Presumably most Growl
notifications are delivered via some local communications channel, which
_is_ per user, which would presumably mean that even a user that was
logged in but not the currently active user would get those. 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. (not such a biggie if non-current users didn't see the
message; just tell 'em not to use fast user switching, let's say...)
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 (unlike using VNC on
a Mac
or Windows, which only remotes the current console session).
So if you want to work in such an environment (not that I'll ever see
one!),
you'd probably have to deal with the possibility of multiple graphical
user
sessions anyway.
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. In which case, perhaps there
should be
a single listener with system-wide policy for network notifications,
which
in turn passes them on via a purely local communications mechanism to
any user GrowlHelperApps running, that can filter them further as they
wish.
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.
Which should make it possible to add an option to allow unauthenticated
network notifications from localhost (127.0.0.1 or ::1 for IPv6), even
if
authentication was required for everyone else. If the UDP listener were
split off, that might be system-level rather than per-user policy too,
which would be better in the case of the terminal server scenario
(keep the
users from spamming each other or using it as an unauthorized inter-user
communications mechanism), although if the authorization status could be
detected after being passed on via the local channel, a user could still
choose to ignore unauthenticated messages even if the system accepted
them.
BTW, I found an existing C binding for the UDP Growl protocol, just a
.c and .h file that are part of something larger. Unfortunately,
they're
(a) GPL'd, and while they'd work for me, (b) they could be generalized
a bit
more to be a truly generic C binding. I've just now sent a message to
the
author asking him (?) to consider making those two files available under
LGPL or BSD, to simplify linking them to minimally modified existing C/
Unix
code, much of which is likely to be BSD, APSL, or CDDL, the latter two
not
being GPL compatible. I'll pass on what I hear. If he doesn't want
to do that,
I'll probably have to whip up something myself. I am thinking that
you guys
really could use a C binding for that, because there are probably a
few other
C apps (small "a", not Mac application bundles) that could also
benefit from
a Growl interface.
Which leads me to one last thing, and I hope this is neither a FAQ nor
inflammatory, as I mean no offense: I wonder why Apple hasn't picked up
Growl and incorporated it as a standard solution to fill in their
shortfall
in that regard? Certainly once some of the cases regarding network
notification
(like multiple users on a terminal server) are solved, I think it
would be
very handy indeed if it were available on every Mac, right out of the
box
(and documented along with the rest of their apps and libraries); that
would
no doubt get a lot more apps using it. I tried to find prior
references to
that particular question, but while a google on
apple incorporated growl
had the first hit being a blog post or something that included that very
question, the rest were way too many non-obviously (from the short
summary
of the match) hits, nor could I think of better keywords to search for.
Ok, having worked a mid shift and now written way too much, I can't keep
my eyes open any longer..._why_ do I always manage to get myself into
too much research and too many design issues? :-) No, that's just
rhetorical,
I'm not really asking you folks that one. But at least you know now why
I've rambled on so much. Believe it or not, I _did_ edit this message
quite
a bit before posting.
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.
Ideally, your rpc.walld could even look in each user's preferences to
find the correct port to use to connect to their Growl.
> … 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
the application asks to know when you click the notification or let it
time out). The long-term solution is sockets, with a two-factor
implementation, such as the one I suggested in my previous message and
you echoed:
> In which case, perhaps there should be a single listener with system-
> wide policy for network notifications, which in turn passes them on
> via a purely local communications mechanism to any user
> GrowlHelperApps running, that can filter them further as they wish.
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
(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.)
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.
> 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.
> Which should make it possible to add an option to allow
> unauthenticated network notifications from localhost (127.0.0.1 or ::
> 1 for IPv6), even if authentication was required for everyone else.
That's the basic idea. GHA should accept notifications locally only
(i.e., only from localhost), whereas the network-notification
receptionist daemon should accept notifications only from the network
(not from localhost).
> Which leads me to one last thing, and I hope this is neither a FAQ
> nor inflammatory, as I mean no offense: I wonder why Apple hasn't
> picked up Growl and incorporated it as a standard solution to fill
> in their shortfall in that regard?
It is sort of a FAQ, although most users who say it phrase it as a
statement. ☺
We agree: It certainly would be cool if they bundled Growl.
> Certainly once some of the cases regarding network notification
> (like multiple users on a terminal server) are solved, I think it
> would be very handy indeed if it were available on every Mac, right
> out of the box (and documented along with the rest of their apps and
> libraries); that
> would no doubt get a lot more apps using it.
Indeed.
> But at least you know now why I've rambled on so much. Believe it
> or not, I _did_ edit this message quite a bit before posting.
I've been there. ☺
>
> 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...
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).