root/trunk/bcfg2/src/lib/Client/Tools/launchd.py

Revision 5580, 5.4 KB (checked in by solj, 5 days ago)

launchd: minor spacing fix

Signed-off-by: Sol Jerome <solj@…>

  • Property svn:keywords set to Revision
Line 
1'''launchd support for Bcfg2'''
2__revision__ = '$Revision$'
3
4import os
5import Bcfg2.Client.Tools
6import popen2
7
8class launchd(Bcfg2.Client.Tools.Tool):
9    '''Support for Mac OS X Launchd Services'''
10    __handles__ = [('Service', 'launchd')]
11    __execs__ = ['/bin/launchctl', '/usr/bin/defaults']
12    name = 'launchd'
13    __req__ = {'Service':['name', 'status']}
14
15    '''
16    currently requires the path to the plist to load/unload,
17    and Name is acually a reverse-fqdn (or the label)
18    '''
19    def __init__(self, logger, setup, config):
20        Bcfg2.Client.Tools.Tool.__init__(self, logger, setup, config)
21
22        '''Locate plist file that provides given reverse-fqdn name
23        /Library/LaunchAgents          Per-user agents provided by the administrator.
24        /Library/LaunchDaemons         System wide daemons provided by the administrator.
25        /System/Library/LaunchAgents   Mac OS X Per-user agents.
26        /System/Library/LaunchDaemons  Mac OS X System wide daemons.'''
27        plistLocations = ["/Library/LaunchDaemons", "/System/Library/LaunchDaemons"]
28        self.plistMapping = {}
29        for directory in plistLocations:
30            for daemon in os.listdir(directory):
31                try:
32                    if daemon.endswith(".plist"):
33                        d = daemon[:-6]
34                    else:
35                        d = daemon
36                    (stdout, _) = popen2.popen2('defaults read %s/%s Label' % (directory, d))
37                    label = stdout.read().strip()
38                    self.plistMapping[label] = "%s/%s" % (directory, daemon)
39                except KeyError: #perhaps this could be more robust
40                    pass
41
42    def FindPlist(self, entry):
43        return self.plistMapping.get(entry.get('name'), None)
44
45    def os_version(self):
46        version = ""
47        try:
48            vers = self.cmd.run('sw_vers')[1]
49        except:
50            return version
51
52        for line in vers:
53            if line.startswith("ProductVersion"):
54                version = line.split()[-1]
55        return version
56
57    def VerifyService(self, entry, _):
58        '''Verify Launchd Service Entry'''
59        try:
60            services = self.cmd.run("/bin/launchctl list")[1]
61        except IndexError:#happens when no services are running (should be never)
62            services = []
63        # launchctl output changed in 10.5
64        # It is now three columns, with the last column being the name of the # service
65        version = self.os_version()
66        if version.startswith('10.5') or version.startswith('10.6'):
67            services = [s.split()[-1] for s in services]
68        if entry.get('name') in services:#doesn't check if non-spawning services are Started
69            return entry.get('status') == 'on'
70        else:
71            self.logger.debug("Didn't find service Loaded (launchd running under same user as bcfg)")
72            return entry.get('status') == 'off'
73
74        try: #Perhaps add the "-w" flag to load and unload to modify the file itself!
75            self.cmd.run("/bin/launchctl load -w %s" % self.FindPlist(entry))
76        except IndexError:
77            return 'on'
78        return False
79
80
81    def InstallService(self, entry):
82        '''Enable or Disable launchd Item'''
83        name = entry.get('name')
84        if entry.get('status') == 'on':
85            self.logger.error("Installing service %s" % name)
86            cmdrc = self.cmd.run("/bin/launchctl load -w %s" % self.FindPlist(entry))[0]
87            cmdrc = self.cmd.run("/bin/launchctl start %s" % name)
88        else:
89            self.logger.error("Uninstalling service %s" % name)
90            cmdrc = self.cmd.run("/bin/launchctl stop %s" % name)
91            cmdrc = self.cmd.run("/bin/launchctl unload -w %s" % self.FindPlist(entry))[0]
92        return cmdrc[0] == 0
93
94    def Remove(self, svcs):
95        '''Remove Extra launchd entries'''
96        pass
97
98
99
100    def FindExtra(self):
101        '''Find Extra launchd Services'''
102        try:
103            allsrv =  self.cmd.run("/bin/launchctl list")[1]
104        except IndexError:
105            allsrv = []
106
107        [allsrv.remove(svc) for svc in [entry.get("name") for entry
108                                        in self.getSupportedEntries()] if svc in allsrv]
109        return [Bcfg2.Client.XML.Element("Service", type='launchd', name=name, status='on') for name in allsrv]
110
111    def BundleUpdated(self, bundle, states):
112        '''Reload launchd plist'''
113        for entry in [entry for entry in bundle if self.handlesEntry(entry)]:
114            if not self.canInstall(entry):
115                self.logger.error("Insufficient information to restart service %s" % (entry.get('name')))
116            else:
117                name = entry.get('name')
118                if entry.get('status') == 'on' and self.FindPlist(entry):
119                    self.logger.info("Reloading launchd service %s" % name)
120                    #stop?
121                    self.cmd.run("/bin/launchctl stop %s" % name)
122                    self.cmd.run("/bin/launchctl unload -w %s" % (self.FindPlist(entry)))#what if it disappeared? how do we stop services that are currently running but the plist disappeared?!
123                    self.cmd.run("/bin/launchctl load -w %s" % (self.FindPlist(entry)))
124                    self.cmd.run("/bin/launchctl start %s" % name)
125                else:
126                    #only if necessary....
127                    self.cmd.run("/bin/launchctl stop %s" % name)
128                    self.cmd.run("/bin/launchctl unload -w %s" % (self.FindPlist(entry)))
Note: See TracBrowser for help on using the browser.