Home | History | Annotate | Download | only in pyautolib
      1 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 # Use of this source code is governed by a BSD-style license that can be
      3 # found in the LICENSE file.
      4 
      5 """Mock pref pane for testing purpose on Mac."""
      6 
      7 import Foundation
      8 import os
      9 import signal
     10 import subprocess
     11 import sys
     12 import tempfile
     13 import time
     14 
     15 
     16 class MockPrefPane(object):
     17   """Mock Pref Pane to enable/disable/changepin without system prompt.
     18 
     19   This only applies to Mac.
     20   """
     21 
     22   def __init__(self):
     23     self._service_name = 'org.chromium.chromoting'
     24     self._real_user_id = os.getuid()
     25     self._config_file = os.path.join(tempfile.gettempdir(),
     26                                      '%s.json' % self._service_name)
     27     self._tool_script = '/Library/PrivilegedHelperTools/%s.me2me.sh' % \
     28         self._service_name
     29 
     30   def _GetJobPid(self):
     31     """Gets the org.chromium.chromoting job id."""
     32     process = subprocess.Popen(['launchctl', 'list'], stdout=subprocess.PIPE)
     33     pid = None
     34     for line in process.stdout:
     35       # Format is:
     36       #   12345  -  my.job       (if my.job is running, number is job's PID)
     37       #   -      0  my.other.job (if my.other.job is not running)
     38       fields = line.strip().split('\t')
     39       if fields[2] == self._service_name and fields[0] != "-":
     40         pid = fields[0]
     41         break
     42     process.wait()
     43     return pid
     44 
     45   def Enable(self):
     46     """Handles what pref pane does for enabling connection."""
     47     # Elevate privileges, otherwise tool_script executes with EUID != 0.
     48     os.setuid(0)
     49     subprocess.call([self._tool_script, '--enable'],
     50                     stdin=open(self._config_file))
     51 
     52     # Drop privileges, start the launchd job as the logged-in user.
     53     os.setuid(self._real_user_id)
     54     subprocess.call(['launchctl', 'start', self._service_name])
     55 
     56     # Starting a launchd job is an asynchronous operation that typically takes
     57     # a couple of seconds, so poll until the job has started.
     58     for _ in range(1, 10):
     59       if self._GetJobPid():
     60         print '*** org.chromium.chromoting is running ***'
     61         break
     62       time.sleep(2)
     63 
     64   def Disable(self):
     65     """Handles what pref pane does for disabling connection."""
     66     # Elevate privileges, otherwise tool_script executes with EUID != 0.
     67     os.setuid(0)
     68     subprocess.call([self._tool_script, '--disable'],
     69                     stdin=open(self._config_file))
     70 
     71     # Drop privileges, stop the launchd job as the logged-in user.
     72     os.setuid(self._real_user_id)
     73     subprocess.call(['launchctl', 'stop', self._service_name])
     74 
     75     # Stopping a launchd job is an asynchronous operation that typically takes
     76     # a couple of seconds, so poll until the job has stopped.
     77     for _ in range(1, 10):
     78       if not self._GetJobPid():
     79         print '*** org.chromium.chromoting is not running ***'
     80         break
     81       time.sleep(2)
     82 
     83   def ChangePin(self):
     84     """Handles what pref pane does for changing pin."""
     85     # Elevate privileges, otherwise tool_script executes with EUID != 0.
     86     os.setuid(0)
     87     subprocess.call([self._tool_script, '--save-config'],
     88                     stdin=open(self._config_file))
     89 
     90     # Drop privileges and send SIGHUP to org.chromium.chromoting
     91     os.setuid(self._real_user_id)
     92     os.kill(int(self._GetJobPid()), signal.SIGHUP)
     93 
     94   def NotifyWebapp(self):
     95     """Notifies the web app that pref pane operation is done."""
     96     notif_center = Foundation.NSDistributedNotificationCenter.defaultCenter()
     97     notif_center.postNotificationName_object_userInfo_(
     98       self._service_name + '.update_succeeded', None, None)
     99 
    100 
    101 def Main():
    102   """Handles the mock pref pane actions."""
    103   assert sys.platform.startswith('darwin')
    104 
    105   print '*** Started mock pref pane ***'
    106   print '*** EUID=%d, UID=%d ***' % (os.geteuid(), os.getuid())
    107 
    108   pref_pane = MockPrefPane()
    109 
    110   if sys.argv[1] == 'enable':
    111     pref_pane.Enable()
    112   elif sys.argv[1] == 'disable':
    113     pref_pane.Disable()
    114   elif sys.argv[1] == 'changepin':
    115     pref_pane.ChangePin()
    116   else:
    117     print >>sys.stderr, 'Invalid syntax'
    118     return
    119 
    120   pref_pane.NotifyWebapp()
    121 
    122 
    123 if __name__ == '__main__':
    124   Main()