overlord: Darwin/XNU compatibility
Go version:
Separate system related utils to sysutils_{GOOS}.go to provide operating
system compatibility for Linux and MacOS X.
Python version:
Use platform.system() to switch between Linux and Darwin
implementations.
BUG=chromium:585733
TEST=`go/src/overlord/test/overlord_e2e_unittest.py`
Change-Id: I0cde3c0996191ac59dc5c5f6a0b4907b53c0dff8
Reviewed-on: https://chromium-review.googlesource.com/327029
Commit-Ready: Wei-Ning Huang <wnhuang@chromium.org>
Tested-by: Wei-Ning Huang <wnhuang@chromium.org>
Reviewed-by: Wei-Han Chen <stimim@chromium.org>
diff --git a/py/tools/ghost.py b/py/tools/ghost.py
index c375d17..0a3e6fb 100755
--- a/py/tools/ghost.py
+++ b/py/tools/ghost.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python -u
+#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright 2015 The Chromium OS Authors. All rights reserved.
@@ -7,11 +7,14 @@
import argparse
import contextlib
+import ctypes
+import ctypes.util
import fcntl
import hashlib
import json
import logging
import os
+import platform
import Queue
import re
import select
@@ -195,6 +198,7 @@
if mode == Ghost.FILE:
assert file_op is not None
+ self._platform = platform.system()
self._overlord_addrs = overlord_addrs
self._connected_addr = None
self._tls_settings = tls_settings
@@ -322,13 +326,14 @@
def CloseSockets(self):
# Close sockets opened by parent process, since we don't use it anymore.
- for fd in os.listdir('/proc/self/fd/'):
- try:
- real_fd = os.readlink('/proc/self/fd/%s' % fd)
- if real_fd.startswith('socket'):
- os.close(int(fd))
- except Exception:
- pass
+ if self._platform == 'Linux':
+ for fd in os.listdir('/proc/self/fd/'):
+ try:
+ real_fd = os.readlink('/proc/self/fd/%s' % fd)
+ if real_fd.startswith('socket'):
+ os.close(int(fd))
+ except Exception:
+ pass
def SpawnGhost(self, mode, sid=None, terminal_sid=None, tty_device=None,
command=None, file_op=None, port=None):
@@ -357,22 +362,31 @@
return int(time.time())
def GetGateWayIP(self):
- with open('/proc/net/route', 'r') as f:
- lines = f.readlines()
+ if self._platform == 'Darwin':
+ output = subprocess.check_output(['route', '-n', 'get', 'default'])
+ ret = re.search('gateway: (.*)', output)
+ if ret:
+ return [ret.group(1)]
+ elif self._platform == 'Linux':
+ with open('/proc/net/route', 'r') as f:
+ lines = f.readlines()
- ips = []
- for line in lines:
- parts = line.split('\t')
- if parts[2] == '00000000':
- continue
+ ips = []
+ for line in lines:
+ parts = line.split('\t')
+ if parts[2] == '00000000':
+ continue
- try:
- h = parts[2].decode('hex')
- ips.append('%d.%d.%d.%d' % tuple(ord(x) for x in reversed(h)))
- except TypeError:
- pass
+ try:
+ h = parts[2].decode('hex')
+ ips.append('%d.%d.%d.%d' % tuple(ord(x) for x in reversed(h)))
+ except TypeError:
+ pass
- return ips
+ return ips
+ else:
+ logging.warning('GetGateWayIP: unsupported platform')
+ return []
def GetShopfloorIP(self):
try:
@@ -390,18 +404,30 @@
def GetMachineID(self):
"""Generates machine-dependent ID string for a machine.
There are many ways to generate a machine ID:
- 1. factory device_id
- 2. factory device-data
- 3. /sys/class/dmi/id/product_uuid (only available on intel machines)
- 4. MAC address
- We follow the listed order to generate machine ID, and fallback to the next
- alternative if the previous doesn't work.
+ Linux:
+ 1. factory device_id
+ 2. factory device-data
+ 3. /sys/class/dmi/id/product_uuid (only available on intel machines)
+ 4. MAC address
+ We follow the listed order to generate machine ID, and fallback to the
+ next alternative if the previous doesn't work.
+
+ Darwin:
+ All Darwin system should have the IOPlatformSerialNumber attribute.
"""
if self._mid == Ghost.RANDOM_MID:
return str(uuid.uuid4())
elif self._mid:
return self._mid
+ # Darwin
+ if self._platform == 'Darwin':
+ output = subprocess.check_output(['ioreg', '-rd1', '-c',
+ 'IOPlatformExpertDevice'])
+ ret = re.search('"IOPlatformSerialNumber" = "(.*)"', output)
+ if ret:
+ return ret.group(1)
+
# Try factory device id
try:
import factory_common # pylint: disable=W0612
@@ -447,6 +473,24 @@
raise RuntimeError('can\'t generate machine ID')
+ def GetProcessWorkingDirectory(self, pid):
+ if self._platform == 'Linux':
+ return os.readlink('/proc/%d/cwd' % pid)
+ elif self._platform == 'Darwin':
+ PROC_PIDVNODEPATHINFO = 9
+ proc_vnodepathinfo_size = 2352
+ vid_path_offset = 152
+
+ proc = ctypes.cdll.LoadLibrary(ctypes.util.find_library('libproc'))
+ buf = ctypes.create_string_buffer('\0' * proc_vnodepathinfo_size)
+ proc.proc_pidinfo(pid, PROC_PIDVNODEPATHINFO, 0,
+ ctypes.byref(buf), proc_vnodepathinfo_size)
+ buf = buf.raw[vid_path_offset:]
+ n = buf.index('\0')
+ return buf[:n]
+ else:
+ raise RuntimeError('GetProcessWorkingDirectory: unsupported platform')
+
def Reset(self):
"""Reset state and clear request handlers."""
if self._sock is not None:
@@ -499,7 +543,7 @@
pid, fd = os.forkpty()
if pid == 0:
- ttyname = os.readlink('/proc/%d/fd/0' % os.getpid())
+ ttyname = os.ttyname(sys.stdout.fileno())
try:
server = GhostRPCServer()
server.RegisterTTY(self._session_id, ttyname)
@@ -801,7 +845,10 @@
if params.has_key('terminal_sid'):
pid = self._terminal_sid_to_pid.get(params['terminal_sid'], None)
if pid:
- target_dir = os.readlink('/proc/%d/cwd' % pid)
+ try:
+ target_dir = self.GetProcessWorkingDirectory(pid)
+ except Exception as e:
+ logging.error(e)
dest_path = os.path.join(target_dir, filename)