| 1 |
'''launchd support for Bcfg2''' |
|---|
| 2 |
__revision__ = '$Revision$' |
|---|
| 3 |
|
|---|
| 4 |
import os |
|---|
| 5 |
import Bcfg2.Client.Tools |
|---|
| 6 |
|
|---|
| 7 |
class launchd(Bcfg2.Client.Tools.Tool): |
|---|
| 8 |
'''Support for Mac OS X Launchd Services''' |
|---|
| 9 |
__handles__ = [('Service', 'launchd')] |
|---|
| 10 |
__execs__ = ['/bin/launchctl', '/usr/bin/defaults'] |
|---|
| 11 |
__name__ = 'launchd' |
|---|
| 12 |
__req__ = {'Service':['name', 'status']} |
|---|
| 13 |
|
|---|
| 14 |
''' |
|---|
| 15 |
currently requires the path to the plist to load/unload, |
|---|
| 16 |
and Name is acually a reverse-fqdn (or the label) |
|---|
| 17 |
''' |
|---|
| 18 |
def FindPlist(self, entry): |
|---|
| 19 |
'''Locate plist file that provides given reverse-fqdn name |
|---|
| 20 |
/Library/LaunchAgents Per-user agents provided by the administrator. |
|---|
| 21 |
/Library/LaunchDaemons System wide daemons provided by the administrator. |
|---|
| 22 |
/System/Library/LaunchAgents Mac OS X Per-user agents. |
|---|
| 23 |
/System/Library/LaunchDaemons Mac OS X System wide daemons.''' |
|---|
| 24 |
plistLocations = ["/Library/LaunchDaemons", "/System/Library/LaunchDaemons"] |
|---|
| 25 |
plistMapping = {} |
|---|
| 26 |
for directory in plistLocations: |
|---|
| 27 |
for daemon in os.listdir(directory): |
|---|
| 28 |
try: |
|---|
| 29 |
if daemon.endswith(".plist"): |
|---|
| 30 |
d = daemon[:(len(daemon)-6)] |
|---|
| 31 |
else: |
|---|
| 32 |
d = daemon |
|---|
| 33 |
plistMapping[self.cmd.run( \ |
|---|
| 34 |
"defaults read %s/%s Label" % (directory, d))[1][0]] = \ |
|---|
| 35 |
"%s/%s"%(directory, daemon) |
|---|
| 36 |
except KeyError: |
|---|
| 37 |
pass |
|---|
| 38 |
try: |
|---|
| 39 |
return plistMapping[entry.get('name')] |
|---|
| 40 |
except KeyError: |
|---|
| 41 |
return None |
|---|
| 42 |
|
|---|
| 43 |
|
|---|
| 44 |
def VerifyService(self, entry, _): |
|---|
| 45 |
'''Verify Launchd Service Entry''' |
|---|
| 46 |
try: |
|---|
| 47 |
services = self.cmd.run("/bin/launchctl list")[1] |
|---|
| 48 |
except IndexError: |
|---|
| 49 |
services = [] |
|---|
| 50 |
if entry.get('name') in services: |
|---|
| 51 |
return entry.get('status') == 'on' |
|---|
| 52 |
else: |
|---|
| 53 |
self.logger.debug("Didn't find service Loaded (launchd running under same user as bcfg)") |
|---|
| 54 |
return entry.get('status') == 'off' |
|---|
| 55 |
|
|---|
| 56 |
try: |
|---|
| 57 |
self.cmd.run("/bin/launchctl load -w %s" % self.FindPlist(entry)) |
|---|
| 58 |
except IndexError: |
|---|
| 59 |
return 'on' |
|---|
| 60 |
return False |
|---|
| 61 |
|
|---|
| 62 |
|
|---|
| 63 |
def InstallService(self, entry): |
|---|
| 64 |
'''Enable or Disable launchd Item''' |
|---|
| 65 |
if entry.get('status') == 'on': |
|---|
| 66 |
cmdrc = self.cmd.run("/bin/launchctl load -w %s" % self.FindPlist(entry))[0] |
|---|
| 67 |
else: |
|---|
| 68 |
cmdrc = self.cmd.run("/bin/launchctl unload -w %s" % self.FindPlist(entry))[0] |
|---|
| 69 |
return cmdrc == 0 |
|---|
| 70 |
|
|---|
| 71 |
def Remove(self, svcs): |
|---|
| 72 |
'''Remove Extra launchd entries''' |
|---|
| 73 |
pass |
|---|
| 74 |
|
|---|
| 75 |
|
|---|
| 76 |
|
|---|
| 77 |
def FindExtra(self): |
|---|
| 78 |
'''Find Extra launchd Services''' |
|---|
| 79 |
try: |
|---|
| 80 |
allsrv = self.cmd.run("/bin/launchctl list")[1] |
|---|
| 81 |
except IndexError: |
|---|
| 82 |
allsrv = [] |
|---|
| 83 |
|
|---|
| 84 |
[allsrv.remove(svc) for svc in [entry.get("name") for entry |
|---|
| 85 |
in self.getSupportedEntries()] if svc in allsrv] |
|---|
| 86 |
return [Bcfg2.Client.XML.Element("Service", type='launchd', name=name, status='on') for name in allsrv] |
|---|
| 87 |
|
|---|
| 88 |
def BundleUpdated(self, bundle, states): |
|---|
| 89 |
'''Reload launchd plist''' |
|---|
| 90 |
for entry in [entry for entry in bundle if self.handlesEntry(entry)]: |
|---|
| 91 |
if not self.canInstall(entry): |
|---|
| 92 |
self.logger.error("Insufficient information to restart service %s" % (entry.get('name'))) |
|---|
| 93 |
else: |
|---|
| 94 |
if entry.get('status') == 'on' and self.FindPlist(entry): |
|---|
| 95 |
self.logger.info("Reloading launchd service %s" % (entry.get("name"))) |
|---|
| 96 |
|
|---|
| 97 |
self.cmd.run("/bin/launchctl unload -w %s" % (self.FindPlist(entry))) |
|---|
| 98 |
self.cmd.run("/bin/launchctl load -w %s" % (self.FindPlist(entry))) |
|---|
| 99 |
else: |
|---|
| 100 |
|
|---|
| 101 |
self.cmd.run("/bin/launchctl unload -w %s" % (self.FindPlist(entry))) |
|---|
| 102 |
|
|---|