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 -~----------~----~----~----~------~----~------~--~---
