├── .gitignore ├── README.md ├── auth_db.py ├── board_id.py ├── cert_check.py ├── ck_passwords.py ├── consoleuser.py ├── corefoundation_example.py ├── diskman.py ├── firewall_status.py ├── generate_macaddress.py ├── genpass.py ├── get_ip.py ├── gethub.py ├── getwifi.py ├── incompatible_apps.py ├── ip_by_cidr.py ├── ip_primary_interface.py ├── lock_screen.py ├── logger.py ├── marketing_model.py ├── mount_shares.py ├── nvram.py ├── password_age.py ├── password_generator.py ├── pid_runtime.py ├── pyappvers.py ├── pybattery.py ├── pycidr.py ├── pydiskutil.py ├── pyfacter.py ├── pyfacts ├── pyioreg-objc.py ├── pyioreg.py ├── pynotify.py ├── pypkgutil.py ├── pypsutil.py ├── pyscreenrez.py ├── pysystemprofiler.py ├── pyvm.py ├── pywifi.py ├── re.py ├── renew_dhcp_lease.py ├── serial_uuid.py ├── set_spotlight_exclusions.py ├── softwareupdate_example.py ├── ssids.py ├── sysctl.py ├── verify_password.py ├── verify_password2.py ├── visible_apps.py ├── vm_status.py └── waitfornetwork.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | todo.txt 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This is a collection of snippets, libraries, scripts, etc, mostly culled from gists by people way smarter than me. If I failed to add a reference link in any of these scripts, please let me know and I will gladly do so. 2 | 3 | Originally the ideas was to create a single module called 'pyfacts' that could be imported into existing projects. While this is still possible, I've abandonded that original intent and am using this repo for experimentation instead. 4 | 5 | pyfacts.py 6 | ======= 7 | 8 | This script returns all or specified facts about your Mac. 9 | 10 | Standalone 11 | ---------- 12 | Use pyfacts.py as a standalone script: 13 | 14 | ./pyfacts.py [key] 15 | 16 | Run with no arguments to return all facts: 17 | 18 | ./pyfacts.py 19 | 20 | Use the list argument to see all available keys: 21 | 22 | ./pyfacts.py -l 23 | 24 | Module 25 | ------ 26 | Import the module into your python script: 27 | 28 | from pyfacts import Facts 29 | 30 | fact = Facts() 31 | serial = fact.get_serial() 32 | print serial 33 | 34 | License 35 | ------- 36 | 37 | Copyright 2016 Joseph Chilcote 38 | 39 | Licensed under the Apache License, Version 2.0 (the "License"); 40 | you may not use this file except in compliance with the License. 41 | You may obtain a copy of the License at 42 | 43 | http://www.apache.org/licenses/LICENSE-2.0 44 | 45 | Unless required by applicable law or agreed to in writing, software 46 | distributed under the License is distributed on an "AS IS" BASIS, 47 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 48 | See the License for the specific language governing permissions and 49 | limitations under the License. 50 | -------------------------------------------------------------------------------- /auth_db.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | #ref: https://gist.github.com/tburgin/77ebd114d59d368b0b4321ca7cf77767 3 | 4 | import objc 5 | from ctypes import CDLL, c_void_p, byref 6 | from ctypes.util import find_library 7 | 8 | Security = CDLL(find_library("Security.framework")) 9 | AuthorizationRightGet = Security.AuthorizationRightGet 10 | AuthorizationRightSet = Security.AuthorizationRightSet 11 | AuthorizationCreate = Security.AuthorizationCreate 12 | 13 | def authorization_right_get(right): 14 | db_buffer = c_void_p() 15 | AuthorizationRightGet(right, byref(db_buffer)) 16 | if db_buffer: 17 | return objc.objc_object(c_void_p=db_buffer).mutableCopy() 18 | return None 19 | 20 | def authorization_right_set(right, value): 21 | auth_ref = c_void_p() 22 | AuthorizationCreate(None, None, 0, byref(auth_ref)) 23 | return AuthorizationRightSet(auth_ref, right, value.__c_void_p__(), None, None, None) 24 | 25 | db = authorization_right_get("system.preferences") 26 | db["group"] = "everyone" 27 | 28 | print db 29 | #authorization_right_set("system.preferences", db) 30 | -------------------------------------------------------------------------------- /board_id.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # ref: https://gist.github.com/bruienne/f81ea88253629abaf5f9 3 | 4 | import objc 5 | import plistlib 6 | 7 | class attrdict(dict): 8 | __getattr__ = dict.__getitem__ 9 | __setattr__ = dict.__setitem__ 10 | 11 | ServerInformation = attrdict() 12 | ServerInformation_bundle = objc.loadBundle('ServerInformation', ServerInformation, \ 13 | bundle_path='/System/Library/PrivateFrameworks/ServerInformation.framework') 14 | 15 | platformsupport = plistlib.readPlist('/System/Library/CoreServices/PlatformSupport.plist') 16 | # 17 | disabledsystems = platformsupport.get('SupportedBoardIds') 18 | # 19 | print('------------------------------------------------------------\n%i Board IDs in list\n------------------------------------------------------------\n' % len(disabledsystems)) 20 | 21 | unmatchedboardids = [] 22 | 23 | for system in disabledsystems: 24 | for modelid in ServerInformation.ServerInformationComputerModelInfo.modelPropertiesForBoardIDs_([system]): 25 | if system not in modelid: 26 | print('Board ID: %s = System ID: %s' % (system, modelid)) 27 | else: 28 | unmatchedboardids.append(system) 29 | 30 | if len(unmatchedboardids) > 0: 31 | print('------------------------------------------------------------') 32 | for boardid in unmatchedboardids: 33 | print('-- No match for Board ID %s --' % boardid) 34 | print('------------------------------------------------------------\n') 35 | -------------------------------------------------------------------------------- /cert_check.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | # ref: https://gist.github.com/pudquick/ad575cee6bb503d4d193da355d70d3e9 4 | 5 | import ssl, base64, objc 6 | from Foundation import NSBundle 7 | Security = NSBundle.bundleWithIdentifier_('com.apple.security') 8 | 9 | S_functions = [ 10 | ('SecCertificateCreateWithData', '@@@'), 11 | ('SecCertificateCopyValues', '@@^@o^@'), 12 | ] 13 | 14 | objc.loadBundleFunctions(Security, globals(), S_functions) 15 | 16 | server_pem = ssl.get_server_certificate(('www.google.com', 443)) 17 | pem_lines = server_pem.splitlines() 18 | pem_base64 = ''.join([x for x in pem_lines if 'CERTIFICATE---' not in x]) 19 | server_der = base64.b64decode(pem_base64) 20 | server_cert = SecCertificateCreateWithData(None, buffer(server_der)) 21 | 22 | cert_details, errors = SecCertificateCopyValues(server_cert, None, None) 23 | 24 | print cert_details 25 | -------------------------------------------------------------------------------- /ck_passwords.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | #ref: https://gist.github.com/pudquick/8d3dedc337161b187a8a1c9564c83463 3 | 4 | # For an alternative method, check out: 5 | # https://gist.github.com/pudquick/3ff4278c609ce223ebb4fc300c5edd0f 6 | 7 | from Foundation import NSBundle, NSClassFromString 8 | SafariShared = NSBundle.bundleWithPath_('/System/Library/PrivateFrameworks/SafariShared.framework') 9 | loaded = SafariShared.load() 10 | WBSPasswordGeneration = NSClassFromString('WBSPasswordGeneration') 11 | CKRecord = NSClassFromString('CKRecord') 12 | 13 | def generate_password(requirements=None): 14 | rec = CKRecord.alloc().initWithRecordType_('pudquick_passwordgen') 15 | if requirements is not None: 16 | # dictionary of values to set passed in 17 | rec.setValuesForKeysWithDictionary_(requirements) 18 | password = WBSPasswordGeneration.generatedPasswordMatchingRequirements_(rec) 19 | # Do a little cleanup, since we don't actually want to save any CloudKit records 20 | del rec 21 | return password 22 | 23 | # Example usage 24 | # >>> generate_password() 25 | # u'hhs-o7X-kaZ-ngw' 26 | # Returns an iCloud Keychain suggested password in the style seen here: 27 | # https://support.apple.com/library/APPLE/APPLECARE_ALLGEOS/Product_Help/en_US/PUBLIC_USERS/PL124/S0700_CloudKeychain.png 28 | 29 | # Alternatively, you can define various keys in a dictionary and pass it in 30 | # >>> r = { 31 | # >>> 'PasswordMinLength': 20, 32 | # >>> 'PasswordMaxLength': 20, # These keys only seem to force length if you set them both to the same value 33 | # >>> 'PasswordRequiredCharacters': ['abc','123','!@#$', 'XYZ'], 34 | # >>> 'PasswordAllowedCharacters': "abc123!@#$XYZ", # allowed characters has to be set if you're requiring sets 35 | # >>> 'PasswordFirstCharacterCandidates': '1a', 36 | # >>> } 37 | # u'11b1c#Z31Y2!b#Z$ZZ#X' 38 | 39 | print generate_password() 40 | -------------------------------------------------------------------------------- /consoleuser.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | from SystemConfiguration import (SCDynamicStoreCopyConsoleUser, 4 | SCDynamicStoreCreate, 5 | SCDynamicStoreCopyValue 6 | ) 7 | 8 | # Using SCDynamicStoreCopyConsoleUser 9 | console_user = SCDynamicStoreCopyConsoleUser(None, None, None)[0] 10 | console_uid = SCDynamicStoreCopyConsoleUser(None, None, None)[1] 11 | console_gid = SCDynamicStoreCopyConsoleUser(None, None, None)[2] 12 | print console_user 13 | print console_uid 14 | print console_gid 15 | 16 | 17 | # Using SCDynamicStoreCreate, SCDynamicStoreCopyValue 18 | net_config = SCDynamicStoreCreate(None, "net", None, None) 19 | console_user = SCDynamicStoreCopyValue(net_config, "State:/Users/ConsoleUser") 20 | #print console_user 21 | print console_user['Name'] 22 | print console_user['UID'] 23 | print console_user['GID'] 24 | 25 | -------------------------------------------------------------------------------- /corefoundation_example.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | #ref: https://macmule.com/2016/12/11/profiles-an-it-admins-best-friend/#more-2645 3 | 4 | from CoreFoundation import ( 5 | CFPreferencesCopyAppValue, 6 | CFPreferencesAppValueIsForced 7 | ) 8 | 9 | domain = 'com.apple.appstore' 10 | key = 'restrict-store-require-admin-to-install' 11 | 12 | key_value = CFPreferencesCopyAppValue(key, domain) 13 | print 'Key Value: %s' % key_value 14 | 15 | key_forced = CFPreferencesAppValueIsForced(key, domain) 16 | print 'Key Forced: %s' % key_forced 17 | 18 | -------------------------------------------------------------------------------- /diskman.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | ## ref: https://gist.github.com/pudquick/5f1baad30024a898e9f2115ac9b0c631 3 | import objc 4 | from Foundation import NSBundle 5 | 6 | # Predefine some opaque types 7 | DASessionRef = objc.createOpaquePointerType('DASessionRef', '^{__DASession=}', None) 8 | DADiskRef = objc.createOpaquePointerType('DADiskRef', '^{__DADisk=}', None) 9 | 10 | # Load DiskManagement framework classes 11 | DiskManagment = objc.loadBundle('DiskManagment', globals(), bundle_path='/System/Library/PrivateFrameworks/DiskManagement.framework') 12 | 13 | # Load DiskArbitration framework functions 14 | DiskArbitration_bundle = NSBundle.bundleWithIdentifier_('com.apple.DiskArbitration') 15 | functions = [ 16 | ('DASessionCreate', '@o@'), 17 | ('DADiskGetBSDName', '*^{__DADisk=}'), 18 | ] 19 | objc.loadBundleFunctions(DiskArbitration_bundle, globals(), functions) 20 | 21 | class diskRef(object): 22 | def __init__(self, dObj, controller, rawRef=False): 23 | if rawRef: 24 | self.cf_type = objc.objc_object(c_void_p=dObj.__pointer__) 25 | self.ref_type = dObj 26 | else: 27 | self.cf_type = dObj 28 | self.ref_type = DADiskRef(c_void_p=(dObj.__c_void_p__().value)) 29 | self.controller = controller 30 | def __repr__(self): 31 | return self.cf_type.__repr__() 32 | @property 33 | def devname(self): 34 | return self.controller.shared.deviceNodeForDisk_error_(self.ref_type, None) 35 | @property 36 | def volname(self): 37 | return self.controller.shared.volumeNameForDisk_error_(self.ref_type, None) 38 | @property 39 | def type(self): 40 | return self.controller.shared.ioContentOfDisk_error_(self.ref_type, None) 41 | @property 42 | def facts(self): 43 | possible = [x for x in dir(self.controller.shared) if (x.startswith('is') and x.endswith('_error_'))] 44 | return [y for y in sorted([x.split('is',1)[-1].rsplit('_error_',1)[0] for x in possible]) if not '_' in y] 45 | @property 46 | def physical_store(self): 47 | # This APFS and other disk types as well supposedly, looking at the code - like CoreStorage, SoftRAID .... 48 | try: 49 | results = self.controller.shared.physicalDisksForDisk_storageSystemName_error_(self.ref_type, None, None) 50 | if results[0] is not None: 51 | return diskRef(results[0], self.controller) 52 | except: 53 | pass 54 | return None 55 | def Is(self, factname): 56 | if factname not in self.facts: 57 | raise Exception('no such fact, check disk.facts') 58 | selector = getattr(self.controller.shared, 'is' + factname + '_error_', None) 59 | if selector is None: 60 | raise Exception('no such fact, check disk.facts') 61 | return (selector)(self.ref_type,None) 62 | 63 | class diskList(object): 64 | # This is dict-list hybrid that allows for slicing as well as lookups by dev name 65 | def __init__(self, disks): 66 | self._disk_list = disks[:] 67 | self._disk_dict = dict() 68 | for i,d in enumerate(disks): 69 | self._disk_dict[d.devname] = d 70 | def __iter__(self): 71 | return iter(self._disk_list) 72 | def __getitem__(self, index): 73 | if isinstance(index, slice): 74 | return self._disk_list.__getitem__(index) 75 | elif isinstance(index, int): 76 | return self._disk_list.__getitem__(index) 77 | elif isinstance(index, diskRef): 78 | # someone passed in a disk, so .. give them back the disk? 79 | return index 80 | return self._disk_dict[index] 81 | def __repr__(self): 82 | return self._disk_list.__repr__() 83 | 84 | class diskManager(object): 85 | def setup_managers(self): 86 | # Create a DiskArb session 87 | session = DASessionCreate(None) 88 | # Get the shared disk manager 89 | self.shared = DMManager.sharedManager() 90 | session_p = DASessionRef(c_void_p=(session.__c_void_p__().value)) 91 | # connect the DA session 92 | self.shared.setDefaultDASession_(session_p) 93 | self.shared.setLanguage_('English') 94 | # init the CS manager 95 | self.shared_cs = DMCoreStorage.alloc().initWithManager_(self.shared) 96 | def __init__(self): 97 | self.shared = None 98 | self.shared_cs = None 99 | self.setup_managers() 100 | def _formatted_disks(self, disk_list): 101 | all_disks = sorted([diskRef(d, self) for d in disk_list], key=lambda x: x.devname) 102 | return diskList(all_disks) 103 | @property 104 | def topLevelDisks(self): 105 | return self._formatted_disks(self.shared.topLevelDisks()) 106 | @property 107 | def disks(self): 108 | return self._formatted_disks(self.shared.disks()) 109 | def diskForPath(self, path): 110 | return diskRef(self.shared.diskForPath_error_(path, None), self, rawRef=True) 111 | 112 | # Example usage 113 | # 114 | # from diskman import diskManager 115 | # dm = diskManager() 116 | # 117 | # >>> dm.disks 118 | # [{id = /dev/disk0}, {id = /dev/disk0s1}, ... 119 | # 120 | # >>> dm.diskForPath('/') 121 | # {id = /dev/disk1} 122 | # 123 | # >>> dm.diskForPath('/').volname 124 | # u'Macintosh HD' 125 | # 126 | # >>> dm.diskForPath('/').devname 127 | # u'/dev/disk1' 128 | # 129 | # >>> dm.disks['/dev/disk1'].facts 130 | # ['AppleDiskImage', 'AppleRAIDDisk', 'AppleRAIDMemberDisk', 'AppleRAIDSetDisk', 'AppleRAIDSpareDisk', 'AppleRAIDUUID', ... 131 | # 132 | # >>> dm.disks['/dev/disk1'].Is('EjectableDisk') 133 | # 0 134 | # 135 | # >>> dm.disks['/dev/disk1'].Is('InternalDisk') 136 | # 1 137 | -------------------------------------------------------------------------------- /firewall_status.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | from Foundation import CFPreferencesCopyAppValue 4 | 5 | plist = '/Library/Preferences/com.apple.alf.plist' 6 | fw_status = CFPreferencesCopyAppValue('globalstate', plist) 7 | 8 | print bool(fw_status) 9 | -------------------------------------------------------------------------------- /generate_macaddress.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import random 4 | 5 | def randomMAC(): 6 | mac = [ 0x00, 0x16, 0x3e, 7 | random.randint(0x00, 0x7f), 8 | random.randint(0x00, 0xff), 9 | random.randint(0x00, 0xff) ] 10 | return ':'.join(map(lambda x: "%02x" % x, mac)) 11 | 12 | print randomMAC() -------------------------------------------------------------------------------- /genpass.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | """Returns a 'memorable' password to facilitate typing off another computers screen 3 | Originally designed for making long/semi-complex, but easier to type passwords 4 | """ 5 | # ref: https://gist.github.com/arubdesu/a3d0087438da6d0f77ab 6 | 7 | 8 | import optparse 9 | import gzip 10 | from random import randrange, choice 11 | 12 | 13 | def chunkstring(string, length): 14 | """list the comprehendo, my friendo""" 15 | return (string[0+i:length+i] for i in range(0, len(string), length)) 16 | 17 | # Read in a range of the same words Password Assistant uses, much thanks to frogor 18 | def words_in_range(short, loong): 19 | """Given a low and high length, return a list of words provided on OS X""" 20 | words_path = '/System/Library/Frameworks/SecurityInterface.framework/Resources/pwa_dict_en.gz' 21 | words_file = gzip.open(words_path, 'rb') 22 | # seek(essentially) to the second 'table' of data that tells you beginning/ends of words 23 | #pylint: disable=unused-variable 24 | ignore = words_file.read(512) 25 | # build our word length counts with the 64 sections as indexes, with the values 26 | # represented in the file as hex - ergo the '16', which int converts 27 | word_counts = dict() 28 | for num_of_letters_index in range(64): 29 | word_counts[num_of_letters_index] = int(words_file.read(8).strip(), 16) 30 | keys = sorted(word_counts.keys()) 31 | 32 | results = [] 33 | for counts in keys: 34 | this_count = word_counts[counts] 35 | if this_count > 0: 36 | # Only look for words if word count is more than 0 37 | # read (the number of words * word length) bytes 38 | raw_words = words_file.read(this_count*counts) 39 | # chunk them up by word length 40 | words = chunkstring(raw_words, counts) 41 | for word in words: 42 | if len(word) >= short and len(word) <= loong: 43 | try: 44 | word.decode('ascii') 45 | results.append(word) 46 | #pylint: disable=bare-except 47 | except: 48 | pass 49 | words_file.close() 50 | return results 51 | 52 | def main(): 53 | """gimme some main""" 54 | #pylint: disable=invalid-name 55 | p = optparse.OptionParser() 56 | p.set_usage("""Usage: %prog [options]""") 57 | p.add_option('--shortest', '-s', dest='shortest_number', default=6, type=int, 58 | help="""(Integer) Number of letters in shortest words to randomly choose.""") 59 | p.add_option('--longest', '-l', dest='longest_number', default=10, type=int, 60 | help="""(Integer) Number of letters in longest words to randomly choose.""") 61 | p.add_option('--total', '-t', dest='total_length', default=22, type=int, 62 | help="""(Integer) Total number of letters in final password.""") 63 | #pylint: disable=unused-variable 64 | options, arguments = p.parse_args() 65 | scoped_words = words_in_range(options.shortest_number, options.longest_number) 66 | full_len = len(scoped_words) 67 | front, back = (scoped_words[randrange(0, full_len)], scoped_words[randrange(0, full_len)]) 68 | diff_string = '' 69 | if len(front + back) < options.total_length: 70 | difference = options.total_length - len(front + back) 71 | while difference > 4: 72 | #pylint: disable=anomalous-backslash-in-string 73 | diff_string += choice('!@#$%&*+=-<>?/\:') 74 | difference -= 1 75 | #pylint: disable=unused-variable 76 | for diff in range(difference): 77 | diff_string += str(randrange(5, 10, 2)) 78 | final_password = ''.join([front, diff_string, back]) 79 | print final_password 80 | 81 | if __name__ == '__main__': 82 | main() 83 | -------------------------------------------------------------------------------- /get_ip.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | #ref: https://gist.github.com/pudquick/c2800ffd06890f67af23 3 | 4 | import socket 5 | 6 | def default_interface(): 7 | # 203.0.113.1 is reserved in TEST-NET-3 per RFC5737 8 | # Should never be local, essentially equal to "internet" 9 | # This should get the 'default' interface 10 | s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 11 | try: 12 | # Uses UDP for instant 'connect' and port 9 for discard 13 | # protocol: http://en.wikipedia.org/wiki/Discard_Protocol 14 | s.connect(('203.0.113.1', 9)) 15 | client = s.getsockname()[0] 16 | except socket.error: 17 | client = "Unknown IP" 18 | finally: 19 | del s 20 | return client 21 | 22 | print default_interface() 23 | -------------------------------------------------------------------------------- /gethub.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import urllib2 4 | import subprocess 5 | import os 6 | import json 7 | 8 | site = 'chilcote' 9 | repo = 'outset' 10 | 11 | url = 'https://api.github.com/repos/%s/%s/releases/latest' % (site, repo) 12 | 13 | response = urllib2.urlopen(url) 14 | d = json.loads(response.read()) 15 | print d['name'] 16 | -------------------------------------------------------------------------------- /getwifi.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import time 4 | import subprocess 5 | 6 | ifconfig_data = subprocess.check_output(["ifconfig", "en2"]) 7 | contains_ip = [i for i in ifconfig_data.split() if i.startswith('192.')] 8 | airport_off = '/usr/sbin/networksetup -setairportpower en2 off' 9 | airport_on = '/usr/sbin/networksetup -setairportpower en2 on' 10 | 11 | while contains_ip == []: 12 | print "No IP, turning Airport Off and On again" 13 | subprocess.call(airport_off.split()) 14 | subprocess.call(airport_on.split()) 15 | time.sleep(20) 16 | ifconfig_data = subprocess.check_output(["ifconfig", "en2"]) 17 | contains_ip = [i for i in ifconfig_data.split() if i.startswith('192.')] 18 | -------------------------------------------------------------------------------- /incompatible_apps.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | #ref https://gist.github.com/bruienne/a66d26602d9b6eb76072 3 | 4 | # pylint: disable=fixme, line-too-long, missing-docstring, C0103 5 | 6 | # Many parts of this were taken from Greg Neagle's COSXIP (https://github.com/munki/createOSXinstallPkg) 7 | # No parsing of 'BannedRegexMatchVersion' keys currently because regex is hard. 8 | # 9 | # Output prints a list of incompatible apps for each major OS X version 10 | # with its version and optional file listing of the target app. 11 | 12 | import plistlib 13 | import subprocess 14 | import sys 15 | import os 16 | from xml.parsers.expat import ExpatError 17 | 18 | LION_PKGNAME = 'MacOS_10_7_IncompatibleAppList.pkg' 19 | LION_CATALOG_URL = ('http://swscan.apple.com/content/catalogs/others/' 20 | 'index-lion-snowleopard-leopard.merged-1.sucatalog') 21 | 22 | MTN_LION_PKGNAME = 'OSX_10_8_IncompatibleAppList.pkg' 23 | MTN_LION_CATALOG_URL = ('https://swscan.apple.com/content/catalogs/others/' 24 | 'index-mountainlion-lion-snowleopard-leopard' 25 | '.merged-1.sucatalog') 26 | 27 | MAVERICKS_PKGNAME = 'OSX_10_9_IncompatibleAppList.pkg' 28 | MAVERICKS_CATALOG_URL = ('https://swscan.apple.com/content/catalogs/others/' 29 | 'index-10.9-mountainlion-lion-snowleopard-leopard' 30 | '.merged-1.sucatalog') 31 | 32 | YOSEMITE_PKGNAME = 'OSX_10_10_IncompatibleAppList.pkg' 33 | YOSEMITE_CATALOG_URL = ( 34 | 'https://swscan.apple.com/content/catalogs/others/' 35 | 'index-10.10-10.9-mountainlion-lion-snowleopard-leopard' 36 | '.merged-1.sucatalog') 37 | 38 | EL_CAPITAN_PKGNAME = 'OSX_10_11_IncompatibleAppList.pkg' 39 | EL_CAPITAN_CATALOG_URL = ( 40 | 'https://swscan.apple.com/content/catalogs/others/' 41 | 'index-10.11-10.10-10.9-mountainlion-lion-snowleopard-leopard' 42 | '.merged-1.sucatalog') 43 | 44 | 45 | def downloadURL(URL, to_file=None): 46 | '''Downloads URL to the current directory or as string''' 47 | cmd = ['/usr/bin/curl', '--silent', '--show-error', '--url', URL] 48 | if to_file: 49 | cmd.extend(['-o', to_file]) 50 | proc = subprocess.Popen(cmd, shell=False, bufsize=-1, 51 | stdin=subprocess.PIPE, 52 | stdout=subprocess.PIPE, stderr=subprocess.PIPE) 53 | (output, err) = proc.communicate() 54 | if proc.returncode: 55 | print >> sys.stderr, 'Error %s retrieving %s' % (proc.returncode, URL) 56 | print >> sys.stderr, err 57 | return None 58 | if to_file: 59 | return to_file 60 | else: 61 | return output 62 | 63 | 64 | def findIncompatibleAppListPkgURL(catalog, package): 65 | '''Searches SU catalog to find a download URL for 66 | package_name. If there's more than one, returns the 67 | one with the most recent PostDate.''' 68 | 69 | def sort_by_PostDate(a, b): 70 | """Internal comparison function for use with sorting""" 71 | return cmp(b['PostDate'], a['PostDate']) 72 | 73 | catalog_str = downloadURL(catalog_url) 74 | try: 75 | catalog = plistlib.readPlistFromString(catalog_str) 76 | except ExpatError: 77 | print >> sys.stderr, 'Could not parse catalog!' 78 | return None 79 | product_list = [] 80 | if 'Products' in catalog: 81 | for product_key in catalog['Products'].keys(): 82 | product = catalog['Products'][product_key] 83 | for package in product.get('Packages', []): 84 | url = package.get('URL', '') 85 | if url.endswith(package_name): 86 | product_list.append({'PostDate': product['PostDate'], 87 | 'URL': url}) 88 | if product_list: 89 | product_list.sort(sort_by_PostDate) 90 | return product_list[0]['URL'] 91 | return None 92 | 93 | 94 | def pkgextract(pkg_source, destination): 95 | 96 | proc = subprocess.Popen(['/usr/sbin/pkgutil', '--expand', pkg_source, destination], 97 | shell=False, bufsize=-1, 98 | stdin=subprocess.PIPE, 99 | stdout=subprocess.PIPE, stderr=subprocess.PIPE) 100 | 101 | (output, err) = proc.communicate() 102 | 103 | return output, err 104 | 105 | 106 | def cpioextract(source, pattern): 107 | 108 | proc = subprocess.Popen(['/usr/bin/cpio', '-idmu', '--quiet', '-I', source, pattern], 109 | shell=False, bufsize=-1, cwd=os.path.dirname(source), 110 | stdin=subprocess.PIPE, 111 | stdout=subprocess.PIPE, stderr=subprocess.PIPE) 112 | 113 | (output, err) = proc.communicate() 114 | 115 | return output, err 116 | 117 | 118 | for os_vers in ['10.7', '10.8', '10.9', '10.10', '10.11']: 119 | 120 | print '\nChecking incompatible apps for OS X %s\n' % os_vers 121 | 122 | if os_vers.startswith('10.7'): 123 | catalog_url = LION_CATALOG_URL 124 | package_name = LION_PKGNAME 125 | os_vers = '10.7' 126 | elif os_vers.startswith('10.8'): 127 | catalog_url = MTN_LION_CATALOG_URL 128 | package_name = MTN_LION_PKGNAME 129 | os_vers = '10.8' 130 | elif os_vers.startswith('10.9'): 131 | catalog_url = MAVERICKS_CATALOG_URL 132 | package_name = MAVERICKS_PKGNAME 133 | os_vers = '10.9' 134 | elif os_vers.startswith('10.10'): 135 | catalog_url = YOSEMITE_CATALOG_URL 136 | package_name = YOSEMITE_PKGNAME 137 | os_vers = '10.10' 138 | elif os_vers.startswith('10.11'): 139 | catalog_url = EL_CAPITAN_CATALOG_URL 140 | package_name = EL_CAPITAN_PKGNAME 141 | os_vers = '10.11' 142 | 143 | url = findIncompatibleAppListPkgURL(catalog_url, package_name) 144 | 145 | package_path = os.path.join('/tmp', package_name) 146 | 147 | if not os.path.exists(package_path): 148 | print 'Downloading pkg %s\n' % package_name 149 | package_path = downloadURL(url, to_file=package_path) 150 | 151 | workdir = os.path.splitext(package_path)[0] 152 | 153 | if not os.path.exists(workdir): 154 | print('Expanding pkg %s to %s\n' % (package_name, workdir)) 155 | pkgextract(package_path, workdir) 156 | 157 | if not os.path.exists(os.path.join(workdir, 'System/Library/PrivateFrameworks/SystemMigration.framework/Versions/A/Resources/MigrationIncompatibleApplicationsList.plist')): 158 | print 'Extracting Payload at %s\n' % workdir 159 | cpioextract(os.path.join(workdir, 'Payload'), "*MigrationIncompatibleApplicationsList.plist*") 160 | 161 | incompatiblesource = plistlib.readPlist(os.path.join( 162 | workdir, 'System/Library/PrivateFrameworks/SystemMigration.framework/Versions/A/Resources/MigrationIncompatibleApplicationsList.plist')) 163 | 164 | incompatibleapps = [] 165 | 166 | for application in incompatiblesource['IncompatiblePaths']: 167 | 168 | if application.get('Application Name'): 169 | if next((item for item in incompatibleapps if item["app"] == application.get('Application Name')), None) is None and application.get('MaximumBundleVersion'): 170 | incompatapp = {'app': application.get('Application Name')} 171 | incompatapp['MaximumBundleVersion'] = application.get( 172 | 'MaximumBundleVersion') or None 173 | if application.get('Paths'): 174 | incompatapp['paths'] = application.get('Paths') 175 | if application.get('DependentPaths'): 176 | if incompatapp.get('paths'): 177 | incompatapp['paths'].append(application.get('DependentPaths')) 178 | else: 179 | incompatapp['paths'] = application.get('DependentPaths') 180 | incompatibleapps.append(incompatapp) 181 | # else: 182 | # print 'Not adding %s' % application.get('Application Name') 183 | 184 | elif application.get('Read Me Group'): 185 | if next((item for item in incompatibleapps if item["app"] == application.get('Read Me Group')), None) is None and application.get('MaximumBundleVersion'): 186 | incompatapp = {'app': application.get('Read Me Group')} 187 | incompatapp['MaximumBundleVersion'] = application.get( 188 | 'MaximumBundleVersion') or None 189 | if application.get('Paths'): 190 | incompatapp['paths'] = application.get('Paths') 191 | if application.get('DependentPaths'): 192 | if incompatapp.get('paths'): 193 | incompatapp['paths'].append(application.get('DependentPaths')) 194 | else: 195 | incompatapp['paths'] = application.get('DependentPaths') 196 | incompatibleapps.append(incompatapp) 197 | # else: 198 | # print 'Not adding %s' % application.get('Read Me Group') 199 | 200 | for app in incompatibleapps: 201 | print app['app'], app.get('MaximumBundleVersion') or '', app.get('paths') or '' 202 | -------------------------------------------------------------------------------- /ip_by_cidr.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import sys, subprocess, socket 4 | 5 | locations = { 6 | '172.31.99.0/23': 'Starbucks', 7 | '172.20.10.0/28': 'Tullys' 8 | } 9 | 10 | def get_activenetwork(): 11 | '''Returns the active network interface of the Mac''' 12 | output = subprocess.check_output(['/usr/sbin/netstat', '-rn']) 13 | for line in output.splitlines(): 14 | if 'default' in line and not 'utun' in line: 15 | return line.split(' ')[-1].strip() 16 | 17 | def get_cidr(activenetwork): 18 | '''Returns the cidr of the current network''' 19 | try: 20 | ip = subprocess.check_output(['/usr/sbin/ipconfig', 'getifaddr', 21 | activenetwork]).strip() 22 | except subprocess.CalledProcessError: 23 | print 'Cannot determine IP Address' 24 | sys.exit(1) 25 | output = subprocess.check_output(['/sbin/ifconfig', activenetwork]) 26 | for line in output.splitlines(): 27 | if ip in line: 28 | netmask = line.split(' ')[3].lower() 29 | 30 | count = int(0) 31 | count+=int(netmask.count('f')) * 4 32 | count+=int(netmask.count('e')) * 3 33 | count+=int(netmask.count('c')) * 2 34 | count+=int(netmask.count('8')) * 1 35 | return '%s.%s/%s' % ('.'.join(ip.split('.')[0:3]), '0', count) 36 | 37 | def ip_in_cidrs(cidr, d): 38 | '''Returns whether the current IP is in a tracked cidr''' 39 | if cidr in d.keys(): return True 40 | 41 | def my_location(cidr, d): 42 | for k, v in d.items(): 43 | if cidr == k: 44 | return v 45 | 46 | def main(): 47 | if get_activenetwork(): 48 | my_cidr = get_cidr(get_activenetwork()) 49 | print True if ip_in_cidrs(my_cidr, locations) else False 50 | print my_location(my_cidr, locations) 51 | 52 | if __name__ == "__main__": 53 | main() 54 | -------------------------------------------------------------------------------- /ip_primary_interface.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | from SystemConfiguration import SCDynamicStoreCreate, SCDynamicStoreCopyValue 4 | 5 | net_config = SCDynamicStoreCreate(None, "net", None, None) 6 | 7 | def get_primaryinterface(): 8 | states = SCDynamicStoreCopyValue(net_config, "State:/Network/Global/IPv4") 9 | return states['PrimaryInterface'] 10 | 11 | def get_ip_address(iface): 12 | addresses = SCDynamicStoreCopyValue(net_config, "State:/Network/Interface/%s/IPv4" % iface) 13 | return addresses['Addresses'][0] 14 | 15 | primary_interface = get_primaryinterface() 16 | print (primary_interface, get_ip_address(primary_interface)) 17 | 18 | print SCDynamicStoreCopyValue(net_config, "State:/Network/Global/IPv4") 19 | print SCDynamicStoreCopyValue(net_config, "State:/Network/Interface") -------------------------------------------------------------------------------- /lock_screen.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | ## ref: https://gist.github.com/pudquick/350ba6411df3be77d32a 3 | 4 | from ctypes import CDLL 5 | 6 | loginPF = CDLL('/System/Library/PrivateFrameworks/login.framework/Versions/Current/login') 7 | result = loginPF.SACLockScreenImmediate() 8 | -------------------------------------------------------------------------------- /logger.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | ''' 4 | Example of how to use argparse to pass an argument to show debug logs 5 | Easier to manage than commenting/uncommenting a bunch of print lines 6 | usage: ./logger.py -ll DEBUG 7 | 8 | Log levels: 9 | DEBUG: shows CRITICAL, ERROR, WARNING, INFO, DEBUG 10 | INFO: shows CRITICAL, ERROR, WARNING, INFO 11 | WARNING: shows CRITICAL, ERROR, WARNING 12 | ERROR: shows CRITICAL, ERROR 13 | CRITICAL: shows CRITICAL 14 | 15 | Default (no arguments) is WARNING 16 | ''' 17 | 18 | from argparse import ArgumentParser 19 | import logging 20 | 21 | def main(): 22 | parser = ArgumentParser(description="Foo app") 23 | parser.add_argument('-ll', '--loglevel', 24 | type=str, 25 | choices=['DEBUG','INFO','WARNING','ERROR','CRITICAL'], 26 | help='Set the logging level') 27 | args = parser.parse_args() 28 | logging.basicConfig(level=args.loglevel) 29 | 30 | logger = logging.getLogger() 31 | 32 | logger.debug('foo debug message') 33 | logger.info('foo info message') 34 | logger.warning('foo warning message') 35 | logger.error('foo error message') 36 | logger.critical('foo critical message') 37 | 38 | if __name__ == '__main__': 39 | main() 40 | -------------------------------------------------------------------------------- /marketing_model.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | #ref: https://gist.github.com/pudquick/098f399d69f230521ef530baca832e76 4 | 5 | # Tested on 10.11 6 | 7 | # Note: 8 | # The marketing information embedded in the ServerInformation.framework is not the same as what 9 | # About This Mac displays - there are differences. 10 | # 11 | # For example: 12 | # ServerInformation: 15" MacBook Pro with Retina display (Mid 2015) 13 | # About This Mac: MacBook Pro (Retina, 15-inch, Mid 2015) 14 | # 15 | # About This Mac will actually hit the internet API to perform the lookup, 16 | # and then saves it locally for future use. 17 | 18 | from Foundation import NSBundle 19 | import xml.etree.ElementTree as ET 20 | import urllib2 21 | 22 | ServerInformation = NSBundle.bundleWithPath_('/System/Library/PrivateFrameworks/ServerInformation.framework') 23 | ServerCompatibility = NSBundle.bundleWithPath_('/System/Library/PrivateFrameworks/ServerCompatibility.framework') 24 | 25 | ServerInformationComputerModelInfo = ServerInformation.classNamed_('ServerInformationComputerModelInfo') 26 | SVCSystemInfo = ServerCompatibility.classNamed_('SVCSystemInfo') 27 | 28 | info = SVCSystemInfo.currentSystemInfo() 29 | extended_info = ServerInformationComputerModelInfo.attributesForModelIdentifier_(info.computerModelIdentifier()) 30 | 31 | if extended_info: 32 | # We don't have to hit the internet API, we have some marketing knowledge 33 | marketing_name = extended_info['marketingModel'] 34 | else: 35 | # Sadly we'll have to reach out 36 | API = urllib2.urlopen('http://support-sp.apple.com/sp/product?cc=' + info.serialNumber()[-4:]) 37 | marketing_name = ET.fromstring(API.read()).find('configCode').text 38 | API.close() 39 | 40 | print marketing_name 41 | -------------------------------------------------------------------------------- /mount_shares.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | #ref https://gist.github.com/pudquick/1362a8908be01e23041d 3 | 4 | import objc, CoreFoundation, Foundation 5 | 6 | class attrdict(dict): 7 | __getattr__ = dict.__getitem__ 8 | __setattr__ = dict.__setitem__ 9 | 10 | NetFS = attrdict() 11 | # Can cheat and provide 'None' for the identifier, it'll just use frameworkPath instead 12 | # scan_classes=False means only add the contents of this Framework 13 | NetFS_bundle = objc.initFrameworkWrapper('NetFS', frameworkIdentifier=None, frameworkPath=objc.pathForFramework('NetFS.framework'), globals=NetFS, scan_classes=False) 14 | 15 | # https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html 16 | # Fix NetFSMountURLSync signature 17 | del NetFS['NetFSMountURLSync'] 18 | objc.loadBundleFunctions(NetFS_bundle, NetFS, [('NetFSMountURLSync', 'i@@@@@@o^@')]) 19 | 20 | def mount_share(share_path): 21 | # Mounts a share at /Volumes, returns the mount point or raises an error 22 | sh_url = CoreFoundation.CFURLCreateWithString(None, share_path, None) 23 | # Set UI to reduced interaction 24 | open_options = {NetFS.kNAUIOptionKey: NetFS.kNAUIOptionNoUI} 25 | # Allow mounting sub-directories of root shares 26 | mount_options = {NetFS.kNetFSAllowSubMountsKey: True} 27 | # Mount! 28 | result, output = NetFS.NetFSMountURLSync(sh_url, None, None, None, open_options, mount_options, None) 29 | # Check if it worked 30 | if result != 0: 31 | raise Exception('Error mounting url "%s": %s' % (share_path, output)) 32 | # Return the mountpath 33 | return str(output[0]) 34 | 35 | def mount_share_at_path(share_path, mount_path): 36 | # Mounts a share at the specified path, returns the mount point or raises an error 37 | sh_url = CoreFoundation.CFURLCreateWithString(None, share_path, None) 38 | mo_url = CoreFoundation.CFURLCreateWithString(None, mount_path, None) 39 | # Set UI to reduced interaction 40 | open_options = {NetFS.kNAUIOptionKey: NetFS.kNAUIOptionNoUI} 41 | # Allow mounting sub-directories of root shares 42 | # Also specify the share should be mounted directly at (not under) mount_path 43 | mount_options = { 44 | NetFS.kNetFSAllowSubMountsKey: True, 45 | NetFS.kNetFSMountAtMountDirKey: True, 46 | } 47 | # Mount! 48 | result, output = NetFS.NetFSMountURLSync(sh_url, mo_url, None, None, open_options, mount_options, None) 49 | # Check if it worked 50 | if result != 0: 51 | raise Exception('Error mounting url "%s" at path "%s": %s' % (share_path, mount_path, output)) 52 | # Return the mountpath 53 | return str(output[0]) 54 | 55 | def mount_share_with_credentials(share_path, username, password): 56 | # Mounts a share at /Volumes, returns the mount point or raises an error 57 | # Include username and password as parameters, not in the share_path URL 58 | sh_url = CoreFoundation.CFURLCreateWithString(None, share_path, None) 59 | # Set UI to reduced interaction 60 | open_options = {NetFS.kNAUIOptionKey: NetFS.kNAUIOptionNoUI} 61 | # Allow mounting sub-directories of root shares 62 | mount_options = {NetFS.kNetFSAllowSubMountsKey: True} 63 | # Mount! 64 | result, output = NetFS.NetFSMountURLSync(sh_url, None, username, password, open_options, mount_options, None) 65 | # Check if it worked 66 | if result != 0: 67 | raise Exception('Error mounting url "%s": %s' % (share_path, output)) 68 | # Return the mountpath 69 | return str(output[0]) 70 | 71 | def mount_share_at_path_with_credentials(share_path, mount_path, username, password): 72 | # Mounts a share at the specified path, returns the mount point or raises an error 73 | # Include username and password as parameters, not in the share_path URL 74 | sh_url = CoreFoundation.CFURLCreateWithString(None, share_path, None) 75 | mo_url = CoreFoundation.CFURLCreateWithString(None, mount_path, None) 76 | # Set UI to reduced interaction 77 | open_options = {NetFS.kNAUIOptionKey: NetFS.kNAUIOptionNoUI} 78 | # Allow mounting sub-directories of root shares 79 | # Also specify the share should be mounted directly at (not under) mount_path 80 | mount_options = { 81 | NetFS.kNetFSAllowSubMountsKey: True, 82 | NetFS.kNetFSMountAtMountDirKey: True, 83 | } 84 | # Mount! 85 | result, output = NetFS.NetFSMountURLSync(sh_url, mo_url, username, password, open_options, mount_options, None) 86 | # Check if it worked 87 | if result != 0: 88 | raise Exception('Error mounting url "%s" at path "%s": %s' % (share_path, mount_path, output)) 89 | # Return the mountpath 90 | return str(output[0]) 91 | -------------------------------------------------------------------------------- /nvram.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # ref https://gist.github.com/pudquick/5a9f5f02c53b3fa648c5704e34b1db8a 3 | 4 | import objc 5 | from Foundation import NSBundle 6 | 7 | IOKit_bundle = NSBundle.bundleWithIdentifier_('com.apple.framework.IOKit') 8 | 9 | functions = [ 10 | ("IORegistryEntryFromPath", b"II*"), 11 | ("IORegistryEntryCreateCFProperty", b"@I@@I"), 12 | ] 13 | 14 | objc.loadBundleFunctions(IOKit_bundle, globals(), functions) 15 | 16 | def nvram(keyname): 17 | raw = IORegistryEntryCreateCFProperty(IORegistryEntryFromPath(0, "IODeviceTree:/options"), keyname, None, 0) 18 | # This returns the raw bytes 19 | # For string values, this will be what you're looking for 20 | # For more complex/structured values, you may need to parse the bytes 21 | return raw.bytes().tobytes() 22 | 23 | # example usage: 24 | # Look up a macOS device serial number via nvram 25 | def serialnumber(): 26 | return nvram("4D1EDE05-38C7-4A6A-9CC6-4BCCA8B38C14:SSN") 27 | 28 | print serialnumber() 29 | -------------------------------------------------------------------------------- /password_age.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | ''' 4 | Started on this script and then like a day later Jamf released a similar script 5 | which does most of what I wanted so I shamelessly raided it: 6 | https://github.com/jamfit/Current-User-Password-Age/blob/master/Current-User-Password-Age.py 7 | ''' 8 | 9 | import os 10 | import sys 11 | import subprocess 12 | import plistlib 13 | import datetime 14 | from SystemConfiguration import SCDynamicStoreCopyConsoleUser 15 | 16 | def get_date_changed(username, creation_date=None): 17 | '''Gets the date of last password change''' 18 | # return None 19 | # for 10.10+ or non-migrated accounts 20 | task = subprocess.check_output(['/usr/bin/dscl', '.', 'read', 'Users/' + username, 'accountPolicyData']) 21 | plist = plistlib.readPlistFromString('\n'.join(task.split()[1:])) 22 | if 'creationTime' in plist.keys(): 23 | creation_date = datetime.datetime.utcfromtimestamp(plist['creationTime']).date() 24 | if 'passwordLastSetTime' in plist.keys(): 25 | return datetime.datetime.utcfromtimestamp(plist['passwordLastSetTime']).date() 26 | else: 27 | # for 10.9.x and lower, or migrated accounts 28 | task = subprocess.Popen(['/usr/bin/dscl', '.', 'read', 'Users/' + username, 'PasswordPolicyOptions'], 29 | stdout=subprocess.PIPE, 30 | stderr=subprocess.PIPE) 31 | (out, err) = task.communicate() 32 | if not err: 33 | plist = plistlib.readPlistFromString('\n'.join(out.split()[1:])) 34 | if 'passwordLastSetTime' in plist.keys(): 35 | return plist['passwordLastSetTime'].date() 36 | return creation_date 37 | 38 | def main(): 39 | username = SCDynamicStoreCopyConsoleUser(None, None, None)[0] 40 | # username = 'luser' 41 | if username: 42 | changed = get_date_changed(username) 43 | if changed: 44 | today = datetime.datetime.utcnow().date() 45 | pw_age = (today - changed).days 46 | print pw_age 47 | else: 48 | print 'Undetermined' 49 | 50 | if __name__ == '__main__': 51 | main() 52 | -------------------------------------------------------------------------------- /password_generator.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | #ref: https://gist.github.com/pudquick/3ff4278c609ce223ebb4fc300c5edd0f 3 | 4 | # Hat tip to https://github.com/anders/pwgen for various initial documentation 5 | # For an alternative method, check out: 6 | # https://gist.github.com/pudquick/8d3dedc337161b187a8a1c9564c83463 7 | 8 | import objc 9 | from Foundation import NSBundle, NSMutableArray, NSString 10 | 11 | SecurityFoundation = NSBundle.bundleWithPath_('/System/Library/Frameworks/SecurityFoundation.framework') 12 | success = SecurityFoundation.load() 13 | 14 | PASS_MIN_LENGTH = 4 15 | PASS_MAX_LENGTH = 1024 16 | PASS_MAX_REQUEST = 15 17 | 18 | algorithm = { 19 | 'memorable': 0, 20 | 'random': 1, 21 | 'letters': 2, # FIPS-181 compliant 22 | 'alphanumeric': 3, 23 | 'numbers': 4, 24 | } 25 | 26 | SFPWAContextRef = objc.createOpaquePointerType("SFPWAContextRef", b"^{_SFPWAContext=}", None) 27 | 28 | functions = [ 29 | ('SFPWAPolicyCopyDefault', '@'), 30 | ('SFPWAContextCreateWithDefaults', '^{_SFPWAContext=}'), 31 | ('SFPWAContextCreate', '^{_SFPWAContext=}'), 32 | ('SFPWAContextLoadDictionaries', 'v^{_SFPWAContext=}^{__CFArray=}I'), 33 | ('SFPWAPasswordSuggest', '@^{_SFPWAContext=}^{__CFDictionary=}IIII'), 34 | ('SFPWAPasswordEvaluator', '@^{_SFPWAContext=}^{__CFString=}I^{__CFDictionary=}'), 35 | ('SFPWAPolicyParse', 'v^{_SFPWAContext=}^{__CFDictionary=}'), 36 | ] 37 | 38 | objc.loadBundleFunctions(SecurityFoundation, globals(), functions) 39 | 40 | def password_languages(): 41 | policy = SFPWAPolicyCopyDefault() 42 | return policy.get('Languages-Evaluate','').split(',') 43 | 44 | def password_suggested_language(): 45 | policy = SFPWAPolicyCopyDefault() 46 | return policy.get('Languages-Suggest','') 47 | 48 | def load_dictionaries(context_ref, language_list): 49 | # The function is a bit picky about making sure we pass a clean NSArray with NSStrings 50 | langs = NSMutableArray.array() 51 | for x in language_list: 52 | langs.addObject_(NSString.stringWithString_(x)) 53 | # The True argument is for whether the language string objects are already retained 54 | SFPWAContextLoadDictionaries(context_ref, langs, True) 55 | 56 | def password_suggest(length=8, count=1, alg=None, langs=None, policy=None): 57 | # The default algorithm is 'memorable' which has issues at lengths beyond 32 characters, FYI 58 | # This will return at a max PASS_MAX_REQUEST (15) passwords at a time 59 | if langs is None: 60 | langs = [password_suggested_language()] 61 | else: 62 | available_langs = password_languages() 63 | for x in langs: 64 | if x not in available_langs: 65 | raise Exception('Error: unavailable language') 66 | if alg is None: 67 | alg = algorithm['memorable'] 68 | else: 69 | alg = algorithm.get(alg, None) 70 | if alg is None: 71 | raise Exception('Error: unavailable algorithm') 72 | if policy is None: 73 | policy = SFPWAPolicyCopyDefault() 74 | context = SFPWAContextCreateWithDefaults() 75 | else: 76 | context = SFPWAContextCreate() 77 | SFPWAPolicyParse(context, policy) 78 | # Language dictionaries are used for memorable-style passwords 79 | load_dictionaries(context, langs) 80 | pass_length = max(min(PASS_MAX_LENGTH, length), PASS_MIN_LENGTH) 81 | pass_count = max(min(PASS_MAX_REQUEST, count), 1) 82 | # The 0 argument is a null argument for callbacks 83 | return SFPWAPasswordSuggest(context, policy, pass_length, 0, pass_count, alg) 84 | 85 | def password_evaluate(password, policy=None, langs=None): 86 | # Check out the return results of SFPWAPolicyCopyDefault() which is just a dict 87 | # You can build your own dict with customized rules 88 | # A key not present in the default policy is 'CharacterSetString' which can be 89 | # set to contain the legal characters allowed by the password policy 90 | # You can set it like: 91 | # policy = SFPWAPolicyCopyDefault() 92 | # policy.update(CharacterSetString="abc123") 93 | if langs is None: 94 | langs = [password_suggested_language()] 95 | else: 96 | available_langs = password_languages() 97 | for x in langs: 98 | if x not in available_langs: 99 | raise Exception('Error: unavailable language') 100 | if policy is None: 101 | policy = SFPWAPolicyCopyDefault() 102 | context = SFPWAContextCreateWithDefaults() 103 | else: 104 | context = SFPWAContextCreate() 105 | SFPWAPolicyParse(context, policy) 106 | load_dictionaries(context, langs) 107 | # The 0 argument is a null argument for callbacks 108 | return SFPWAPasswordEvaluator(context, password, 0, policy) 109 | 110 | 111 | print password_suggest() 112 | print password_evaluate('Dan4(ins') 113 | -------------------------------------------------------------------------------- /pid_runtime.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | #ref https://gist.github.com/pudquick/593fda0fd9e0191c748ac00cd4359702 3 | 4 | from ctypes import CDLL, util, c_int, c_uint, byref, sizeof 5 | import time 6 | 7 | # We just need a large enough buffer for the result 8 | # On 64-bit systems, this struct is 648 bytes, so 1024 bytes is enough 9 | BUFFER_SIZE = 1024 10 | 11 | CTL_KERN = 1 12 | KERN_PROC = 14 13 | KERN_PROC_PID = 1 14 | 15 | libc = CDLL(util.find_library("c")) 16 | 17 | def starttime_for_pid(pid): 18 | mib = (c_int*4)(CTL_KERN, KERN_PROC, KERN_PROC_PID, pid) 19 | # allocate the buffer as an array of unsigned integers 20 | # We're fortunate in that kp_proc.p_starttime.tv_sec is 21 | # the very first value in this data structure 22 | proc_buffer = (c_uint*(BUFFER_SIZE/sizeof(c_uint)))() 23 | size = c_int(BUFFER_SIZE) 24 | result = libc.sysctl(mib, 4, byref(proc_buffer), byref(size), 0, 0) 25 | return proc_buffer[0] 26 | 27 | def seconds_running(pid): 28 | start_time = starttime_for_pid(pid) 29 | if start_time == 0: 30 | # Process id is not valid 31 | return -1 32 | return time.time() - start_time 33 | 34 | print seconds_running(504) 35 | -------------------------------------------------------------------------------- /pyappvers.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | ''' 4 | This script returns the version of the specified Application 5 | 6 | usage: 7 | ./pyappvers.py "VMware Fusion" 8 | 9 | Documentation: 10 | https://developer.apple.com/library/mac/documentation/CoreFoundation/Reference/CFPreferencesUtils/Reference/reference.html 11 | ''' 12 | 13 | ############################################################################## 14 | # Copyright 2014 Joseph Chilcote 15 | # 16 | # Licensed under the Apache License, Version 2.0 (the "License"); you may not 17 | # use this file except in compliance with the License. You may obtain a copy 18 | # of the License at 19 | # 20 | # http://www.apache.org/licenses/LICENSE-2.0 21 | # 22 | # Unless required by applicable law or agreed to in writing, software 23 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 24 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 25 | # License for the specific language governing permissions and limitations 26 | # under the License. 27 | ############################################################################## 28 | 29 | import os, sys, glob, re 30 | from CoreFoundation import CFPreferencesCopyAppValue 31 | 32 | DEBUG = False 33 | app_list = [] 34 | app_list.extend(glob.glob('/Applications/' '*.app')) 35 | app_list.extend(glob.glob('/Applications/*/' '*.app')) 36 | app_list.extend(glob.glob('/Users/*/Applications/' '*.app')) 37 | 38 | def usage(): 39 | print 'Usage: ./pyappvers.py APPLICATION' 40 | 41 | def debug(name, path, plist, version): 42 | print app_list 43 | print "Application name: %s" % name 44 | print 'Application path: %s' % path 45 | print 'Info.plist path: %s' % plist 46 | print 'Version: %s' % version 47 | 48 | def main(): 49 | app_path = '' 50 | if not len(sys.argv) == 2: 51 | usage() 52 | sys.exit(0) 53 | app_name = sys.argv[1] + '.app' 54 | for app in app_list: 55 | if app_name in app: 56 | app_path = app 57 | if not os.path.exists(app_path): 58 | print "Can't find that application!" 59 | sys.exit(0) 60 | plist = os.path.join(app_path, 'Contents/Info.plist') 61 | if not os.path.exists(plist): 62 | print "Can't find the Info.plist!" 63 | sys.exit(0) 64 | version = CFPreferencesCopyAppValue('CFBundleShortVersionString', plist) 65 | if not version: 66 | print "No version information found!" 67 | sys.exit(0) 68 | if not DEBUG: 69 | print version 70 | sys.exit(0) 71 | debug(app_name, app_path, plist, version) 72 | 73 | if __name__ == "__main__": 74 | main() 75 | -------------------------------------------------------------------------------- /pybattery.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | #ref: https://gist.github.com/pudquick/134acb5f7423312effcc98ec56679136 3 | 4 | import objc 5 | from Foundation import NSBundle 6 | 7 | IOKit = NSBundle.bundleWithIdentifier_('com.apple.framework.IOKit') 8 | 9 | functions = [("IOServiceGetMatchingService", b"II@"), 10 | ("IOServiceMatching", b"@*"), 11 | ("IORegistryEntryCreateCFProperties", b"IIo^@@I"), 12 | ("IOPSCopyPowerSourcesByType", b"@I"), 13 | ("IOPSCopyPowerSourcesInfo", b"@"), 14 | ] 15 | 16 | objc.loadBundleFunctions(IOKit, globals(), functions) 17 | 18 | # matches information pulled by: pmset -g rawbatt 19 | def raw_battery_dict(): 20 | battery = IOServiceGetMatchingService(0, IOServiceMatching("AppleSmartBattery")) 21 | if (battery != 0): 22 | # we have a battery 23 | err, props = IORegistryEntryCreateCFProperties(battery, None, None, 0) 24 | return props 25 | 26 | # matches information pulled by: pmset -g batt 27 | def adjusted_battery_dict(): 28 | try: 29 | battery = list(IOPSCopyPowerSourcesByType(0))[0] 30 | except: 31 | battery = 0 32 | if (battery != 0): 33 | # we have a battery 34 | return battery 35 | 36 | def raw_battery_percent(): 37 | d = raw_battery_dict() 38 | if d: 39 | curc = d['CurrentCapacity'] 40 | maxc = d['MaxCapacity'] 41 | perc = 100.*curc/maxc 42 | return perc 43 | 44 | def adjusted_battery_percent(): 45 | d = adjusted_battery_dict() 46 | if d: 47 | return d["Current Capacity"] 48 | 49 | 50 | print raw_battery_dict() 51 | print adjusted_battery_dict() 52 | print raw_battery_percent() 53 | print adjusted_battery_percent() 54 | -------------------------------------------------------------------------------- /pycidr.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import subprocess 4 | from SystemConfiguration import SCDynamicStoreCreate, SCDynamicStoreCopyValue 5 | 6 | def get_interface(net_config): 7 | '''Returns the active network interface of the Mac''' 8 | try: 9 | states = SCDynamicStoreCopyValue(net_config, "State:/Network/Global/IPv4") 10 | return states['PrimaryInterface'] 11 | except TypeError: 12 | print TypeError 13 | exit(1) 14 | 15 | def get_ip(net_config, interface): 16 | '''Returns the IP address of the primary network interface''' 17 | addresses = SCDynamicStoreCopyValue(net_config, "State:/Network/Interface/%s/IPv4" % interface) 18 | try: 19 | return addresses['Addresses'][0] 20 | except TypeError: 21 | print TypeError 22 | exit(1) 23 | 24 | def main(): 25 | net_config = SCDynamicStoreCreate(None, "net", None, None) 26 | print SCDynamicStoreCopyValue(net_config, "State:/Network/Global/DNS") 27 | interface = get_interface(net_config) 28 | ip = get_ip(net_config, interface) 29 | 30 | output = subprocess.check_output(['/sbin/ifconfig', interface]) 31 | for line in output.splitlines(): 32 | if ip in line: 33 | netmask = line.split(' ')[3].lower() 34 | 35 | count = int(0) 36 | count+=int(netmask.count('f')) * 4 37 | count+=int(netmask.count('e')) * 3 38 | count+=int(netmask.count('c')) * 2 39 | count+=int(netmask.count('8')) * 1 40 | 41 | print '%s.%s/%s' % ('.'.join(ip.split('.')[0:3]), '0', count) 42 | 43 | if __name__ == "__main__": 44 | main() 45 | -------------------------------------------------------------------------------- /pydiskutil.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | ''' 4 | This script collects data from diskutil 5 | ''' 6 | 7 | ############################################################################## 8 | # Copyright 2014 Joseph Chilcote 9 | # 10 | # Licensed under the Apache License, Version 2.0 (the "License"); you may not 11 | # use this file except in compliance with the License. You may obtain a copy 12 | # of the License at 13 | # 14 | # http://www.apache.org/licenses/LICENSE-2.0 15 | # 16 | # Unless required by applicable law or agreed to in writing, software 17 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 18 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 19 | # License for the specific language governing permissions and limitations 20 | # under the License. 21 | ############################################################################## 22 | 23 | import os, sys, subprocess 24 | import plistlib 25 | 26 | class Diskutil(object): 27 | 28 | def __init__(self): 29 | '''Initialize object''' 30 | self.data = self.get_data() 31 | 32 | def get_data(self): 33 | '''Returns a dict of all diskutil data''' 34 | cmd = '/usr/sbin/diskutil', 'list', '-plist' 35 | task = subprocess.Popen(cmd, stdout=subprocess.PIPE, 36 | stderr=subprocess.PIPE) 37 | (out, err) = task.communicate() 38 | plist = plistlib.readPlistFromString(out) 39 | return plist 40 | 41 | def get_alldisks(self): 42 | '''Returns a dict of all disk identifiers''' 43 | d = self.data 44 | d1 = {} 45 | d1['All Disks'] = d['AllDisks'] 46 | return d1 47 | 48 | def get_wholedisks(self): 49 | '''Returns a dict of all whole disk identifiers''' 50 | d = self.data 51 | d1 = {} 52 | d1['Whole Disks'] = d['WholeDisks'] 53 | return d1 54 | 55 | def get_allpartitions(self): 56 | '''Returns a dict of all disk partitions''' 57 | d = self.data 58 | d1 = {} 59 | d1['Partitions'] = d['AllDisksAndPartitions'] 60 | return d1 61 | 62 | def get_volumes(self): 63 | '''Returns a dict of all mounted volumes''' 64 | d = self.data 65 | d1 = {} 66 | d1['Volumes'] = d['VolumesFromDisks'] 67 | return d1 68 | 69 | def get_partitions(self): 70 | '''Returns a dict of all partitions''' 71 | d = self.data 72 | d1 = {} 73 | disks = d['AllDisks'] 74 | partitions = d['AllDisksAndPartitions'] 75 | for partition in partitions: 76 | if 'Partitions' in partition: 77 | for p in partition['Partitions']: 78 | if 'MountPoint' in p: 79 | d1[p['DeviceIdentifier']] = ( 80 | p['VolumeName'], 81 | p['Content'], 82 | p['Size'], 83 | p['MountPoint'] 84 | ) 85 | elif 'VolumeName' in p and not 'MountPoint' in p: 86 | d1[p['DeviceIdentifier']] = ( 87 | p['VolumeName'], 88 | p['Content'], 89 | p['Size'], 90 | '' 91 | ) 92 | else: 93 | d1[p['DeviceIdentifier']] = ( 94 | '', 95 | p['Content'], 96 | p['Size'], 97 | '' 98 | ) 99 | else: 100 | d1[partition['DeviceIdentifier']] = ( 101 | partition['VolumeName'], 102 | partition['Content'], 103 | partition['Size'], 104 | partition['MountPoint'] 105 | ) 106 | return d1 107 | 108 | def get_bootvolume(self): 109 | '''Returns the boot volume''' 110 | d = {} 111 | p = self.get_partitions() 112 | for k, v in p.items(): 113 | if '/' in v: 114 | d['Boot Volume'] = v[0] 115 | return d 116 | 117 | def main(): 118 | du = Diskutil() 119 | # print du.get_data() 120 | # print du.get_alldisks() 121 | # print du.get_wholedisks() 122 | vol = du.get_volumes() 123 | for k, v in vol.items(): 124 | print '%s: %s' % (k, v) 125 | bv = du.get_bootvolume() 126 | for k, v in bv.items(): 127 | print '%s: %s' % (k, v) 128 | p = du.get_partitions() 129 | for k, v in sorted(p.items()): 130 | print '%s: %s' % (k, v) 131 | print du.get_alldisks() 132 | print du.get_wholedisks() 133 | 134 | if __name__ == "__main__": 135 | main() 136 | -------------------------------------------------------------------------------- /pyfacter.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | ''' 4 | this script imports all facts from facter into an array and either returns 5 | all facts or a specified fact. 6 | 7 | facter is required on the local system 8 | http://docs.puppetlabs.com/facter/2.0/release_notes.html 9 | ''' 10 | 11 | ############################################################################## 12 | # Copyright 2014 Joseph Chilcote 13 | # 14 | # Licensed under the Apache License, Version 2.0 (the "License"); you may not 15 | # use this file except in compliance with the License. You may obtain a copy 16 | # of the License at 17 | # 18 | # http://www.apache.org/licenses/LICENSE-2.0 19 | # 20 | # Unless required by applicable law or agreed to in writing, software 21 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 22 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 23 | # License for the specific language governing permissions and limitations 24 | # under the License. 25 | ############################################################################## 26 | 27 | import subprocess, sys, json 28 | 29 | class Facter(object): 30 | '''Facter object''' 31 | 32 | def __init__(self): 33 | p = subprocess.Popen( 34 | ['facter', '--json'], 35 | stdout=subprocess.PIPE, 36 | stderr=subprocess.PIPE 37 | ) 38 | out, err = p.communicate() 39 | self.data = json.loads(out) 40 | 41 | def get(self, key): 42 | return self.data[key] 43 | 44 | def all(self): 45 | for k, v in sorted(self.data.items()): 46 | print "%s: %s" % (k, v) 47 | 48 | def main(): 49 | if len(sys.argv) > 2: 50 | print "Usage: ./pyfacter.py " 51 | sys.exit(0) 52 | facts = Facter() 53 | if len(sys.argv) == 1: 54 | facts.all() 55 | elif len(sys.argv) == 2: 56 | print facts.get(sys.argv[1]) 57 | 58 | if __name__ == "__main__": 59 | main() 60 | -------------------------------------------------------------------------------- /pyfacts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | ''' 4 | This script returns all or specified facts about your Mac. 5 | 6 | Requirements 7 | ------------ 8 | + OS X 10.9.x 9 | + python 2.7 10 | 11 | ''' 12 | 13 | ############################################################################## 14 | # Copyright 2014 Joseph Chilcote 15 | # 16 | # Licensed under the Apache License, Version 2.0 (the "License"); you may not 17 | # use this file except in compliance with the License. You may obtain a copy 18 | # of the License at 19 | # 20 | # http://www.apache.org/licenses/LICENSE-2.0 21 | # 22 | # Unless required by applicable law or agreed to in writing, software 23 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 24 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 25 | # License for the specific language governing permissions and limitations 26 | # under the License. 27 | ############################################################################## 28 | 29 | import os 30 | import re 31 | import sys 32 | import socket 33 | import platform 34 | import sysconfig 35 | import subprocess 36 | import argparse 37 | import plistlib 38 | import datetime 39 | import urllib2 40 | import json 41 | from collections import Counter 42 | from AppKit import NSScreen 43 | from CoreFoundation import CFPreferencesCopyAppValue 44 | import objc 45 | # from SystemConfiguration import * 46 | from SystemConfiguration import SCPreferencesCreate, \ 47 | SCNetworkServiceCopyAll, \ 48 | SCNetworkInterfaceCopyAll, \ 49 | SCDynamicStoreCopyComputerName, \ 50 | SCDynamicStoreCopyLocalHostName, \ 51 | SCDynamicStoreCopyConsoleUser, \ 52 | SCNetworkInterfaceGetBSDName, \ 53 | SCNetworkInterfaceGetHardwareAddressString, \ 54 | SCNetworkInterfaceGetLocalizedDisplayName, \ 55 | SCNetworkServiceGetName, \ 56 | SCDynamicStoreCreate, \ 57 | SCDynamicStoreCopyValue 58 | 59 | objc.loadBundle('CoreWLAN', 60 | bundle_path='/System/Library/Frameworks/CoreWLAN.framework', 61 | module_globals=globals()) 62 | 63 | class Facts(object): 64 | ''' 65 | Facts object 66 | Import the module into your python script: 67 | 68 | from pyfacts import Facts 69 | 70 | fact = Facts() 71 | serial = fact.get_serial() 72 | print serial 73 | ''' 74 | 75 | def __init__(self): 76 | self.facts = {} 77 | self.prefs = SCPreferencesCreate(None, 'foo', None) 78 | self.network_services = SCNetworkServiceCopyAll(self.prefs) 79 | self.network_interfaces = SCNetworkInterfaceCopyAll() 80 | self.wifi = CWInterface.interfaceNames() 81 | self.net_config = SCDynamicStoreCreate(None, "net", None, None) 82 | self.primaryinterface = self.get_primaryinterface() 83 | self.hardwaredatatype = plistlib.readPlistFromString( 84 | subprocess.check_output( 85 | ['/usr/sbin/system_profiler', 86 | 'SPHardwareDataType', 87 | '-xml'] 88 | ))[0]['_items'][0] 89 | if self.wifi: 90 | for iname in self.wifi: 91 | self.interface = CWInterface.interfaceWithName_(iname) 92 | 93 | def get_script(self): 94 | '''Returns the name of this script''' 95 | try: 96 | return sys.argv[0] 97 | except: 98 | return None 99 | 100 | def get_hostname(self): 101 | '''Returns the hostname of this Mac''' 102 | # hostname = SCDynamicStoreCopyLocalHostName(None) 103 | sys_info = SCDynamicStoreCopyValue(self.net_config, "Setup:/System") 104 | return sys_info['HostName'] 105 | 106 | def get_computername(self): 107 | '''Returns the ComputerName of this Mac''' 108 | # computername = SCDynamicStoreCopyComputerName(None, None)[0] 109 | sys_info = SCDynamicStoreCopyValue(self.net_config, "Setup:/System") 110 | return sys_info['ComputerName'] 111 | 112 | def get_kernelversion(self): 113 | '''Returns the darwin version of this Mac 114 | example: 13.1.0''' 115 | return platform.release() 116 | 117 | def get_productversion(self): 118 | '''Returns the os x version of this Mac 119 | example: 10.9.2''' 120 | return platform.mac_ver()[0] 121 | 122 | def get_productversionmajor(self): 123 | '''Returns the major os x version of this Mac''' 124 | return '.'.join(platform.mac_ver()[0].split('.')[0:2]) 125 | 126 | def get_productversionminor(self): 127 | '''Returns the minor os x version of this Mac''' 128 | return platform.mac_ver()[0].split('.')[-1] 129 | 130 | def get_pythonversion(self): 131 | '''Returns the python version on this Mac''' 132 | return platform.python_version() 133 | 134 | def get_architecture(self): 135 | '''Returns the architecture of this Mac 136 | example: x86_64''' 137 | return platform.machine() 138 | 139 | def get_platform(self): 140 | '''Returns the processor type of this Mac 141 | exxample: darwin''' 142 | return sys.platform 143 | 144 | def get_id(self): 145 | '''Returns the current console user on this Mac''' 146 | ## Using SystemConfiguration: 147 | # return SCDynamicStoreCopyConsoleUser(None, None, None)[0] 148 | return os.getlogin() 149 | 150 | def get_user(self): 151 | script = ['/usr/bin/last'] 152 | users = [] 153 | get_last = subprocess.check_output(script) 154 | for line in get_last.splitlines(): 155 | if line != '': 156 | name = line.split() 157 | users.append(name[0]) 158 | users = Counter(users).most_common() 159 | return users[0][0] 160 | 161 | def get_uname(self): 162 | '''Returns the full uname of this Mac as a tuple''' 163 | return os.uname() 164 | 165 | def get_sysinfo(self): 166 | '''Returns the system info of this Mac''' 167 | return os.uname()[3] 168 | 169 | def get_homedir(self): 170 | '''Returns the home directory of the current user''' 171 | return os.environ['HOME'] 172 | 173 | def get_prompt(self): 174 | '''Returns the prompt env of the current user''' 175 | try: 176 | pc = os.environ['PROMPT_COMMAND'] 177 | except KeyError: 178 | pc = None 179 | return pc 180 | 181 | def get_path(self): 182 | '''Returns the path env of the current user''' 183 | return os.environ['PATH'] 184 | 185 | def get_term(self): 186 | '''Returns the term env of the current user''' 187 | return os.environ['TERM'] 188 | 189 | def get_termprogram(self): 190 | '''Returns the term program env of the current user''' 191 | try: 192 | tp = os.environ['TERM_PROGRAM'] 193 | except KeyError: 194 | tp = None 195 | return tp 196 | 197 | def get_shell(self): 198 | '''Returns the shell env of the current user''' 199 | return os.environ['SHELL'] 200 | 201 | def get_pythoninterpreter(self): 202 | '''Returns the python interpreter on this Mac''' 203 | return os.environ['VERSIONER_PYTHON_VERSION'] 204 | 205 | def get_editor(self): 206 | '''Returns the editor env of the current user''' 207 | try: 208 | ed = os.environ['EDITOR'] 209 | except KeyError: 210 | ed = None 211 | return ed 212 | 213 | def get_pwd(self): 214 | '''Returns the pwd''' 215 | try: 216 | pwd = os.environ['PWD'] 217 | except KeyError: 218 | pwd = None 219 | return pwd 220 | 221 | def get_tmpdir(self): 222 | '''Returns the tempdir env of the current user''' 223 | try: 224 | td = os.environ['TMPDIR'] 225 | except KeyError: 226 | td = None 227 | return td 228 | 229 | def get_cwd(self): 230 | '''Returns the cwd''' 231 | return os.getcwd() 232 | 233 | def get_euid(self): 234 | '''Returns the euid of the current user''' 235 | return os.geteuid() 236 | 237 | def get_uid(self): 238 | '''Returns the uid of the current user''' 239 | ## Using SystemConfiguration: 240 | # return SCDynamicStoreCopyConsoleUser(None, None, None)[1] 241 | return os.getuid() 242 | 243 | def get_egid(self): 244 | '''Returns the egid of the current user''' 245 | return os.getegid() 246 | 247 | def get_gid(self): 248 | '''Returns the gid of the current user''' 249 | ## Using SystemConfiguration: 250 | # return SCDynamicStoreCopyConsoleUser(None, None, None)[2] 251 | return os.getgid() 252 | 253 | def get_groups(self): 254 | '''Returns the groups of the current user''' 255 | return os.getgroups() 256 | 257 | def get_ip(self): 258 | '''Returns the IP address of the primary network interface''' 259 | # interface = self.get_primaryinterface() 260 | # try: 261 | # ip = subprocess.check_output(['/usr/sbin/ipconfig', 'getifaddr', interface]) 262 | # except subprocess.CalledProcessError: 263 | # ip = 'Unknown' 264 | # return ip.strip() 265 | addresses = SCDynamicStoreCopyValue(self.net_config, "State:/Network/Interface/%s/IPv4" % self.primaryinterface) 266 | try: 267 | return addresses['Addresses'][0] 268 | except TypeError: 269 | return None 270 | 271 | def get_external_ip(self): 272 | url = 'http://ipecho.net/plain' 273 | try: 274 | ip = urllib2.urlopen(url).read() 275 | return ip.strip() 276 | except urllib2.HTTPError as err: 277 | return 'Cannot connect to %s: %s' % (url, err) 278 | return 'Unknown' 279 | 280 | def get_networkservices(self): 281 | '''Returns a list of all network interface names''' 282 | l = [] 283 | for interface in self.network_interfaces: 284 | l.append(SCNetworkInterfaceGetLocalizedDisplayName(interface)) 285 | return l 286 | 287 | def get_dns_domain(self): 288 | '''Returns the current dns domain''' 289 | dns_info = SCDynamicStoreCopyValue(self.net_config, "State:/Network/Global/DNS") 290 | if dns_info.get('DomainName'): 291 | return dns_info['DomainName'] 292 | 293 | def get_searchdomains(self): 294 | '''Returns the current search domains''' 295 | l = [] 296 | dns_info = SCDynamicStoreCopyValue(self.net_config, "State:/Network/Global/DNS") 297 | if dns_info and dns_info.get('SearchDomains'): 298 | try: 299 | for i in dns_info['SearchDomains']: 300 | l.append(i) 301 | return l 302 | except KeyError as err: 303 | return 'No search domains found' 304 | 305 | def get_dns_servers(self): 306 | '''Returns the current dns servers''' 307 | l = [] 308 | dns_info = SCDynamicStoreCopyValue(self.net_config, "State:/Network/Global/DNS") 309 | if dns_info: 310 | for i in dns_info['ServerAddresses']: 311 | l.append(i) 312 | return l 313 | 314 | def get_searchdomains_from_dhcp(self): 315 | '''Returns a list of search domains''' 316 | interface = self.get_primaryinterface() 317 | cmd = ['/usr/sbin/ipconfig', 'getpacket', interface] 318 | output = subprocess.check_output(cmd) 319 | for line in output.splitlines(): 320 | if 'domain_search' in line: 321 | return re.sub('[{}]', '', line.split(': ')[1]) 322 | return 'None' 323 | 324 | def get_networkinterfacelist(self): 325 | '''Returns a list of all network interface names''' 326 | d = {} 327 | for interface in self.network_interfaces: 328 | d[SCNetworkInterfaceGetLocalizedDisplayName(interface)] = ( 329 | SCNetworkInterfaceGetBSDName(interface), 330 | SCNetworkInterfaceGetHardwareAddressString(interface) 331 | ) 332 | return d 333 | 334 | def get_wifiinterface(self): 335 | '''Returns the name of the Wi-Fi network interface''' 336 | interface = self.get_networkinterfacelist() 337 | try: 338 | return interface['Wi-Fi'][0] 339 | except KeyError: 340 | return None 341 | 342 | def get_wifimacaddress(self): 343 | '''Returns the MAC address of the Wi-Fi network interface''' 344 | interface = self.get_networkinterfacelist() 345 | try: 346 | return interface['Wi-Fi'][1] 347 | except KeyError: 348 | return None 349 | 350 | def get_wifistatus(self): 351 | '''Returns the wifi status 352 | Yes or No''' 353 | try: 354 | if self.interface.powerOn() == 1: 355 | return "Yes" 356 | return "No" 357 | except AttributeError: 358 | return None 359 | 360 | def get_ssid(self): 361 | '''Returns the SSID of the Wi-Fi interface''' 362 | try: 363 | return self.interface.ssid() 364 | except AttributeError: 365 | return None 366 | 367 | def get_serial(self): 368 | '''Returns the serial number of the Mac''' 369 | output = subprocess.check_output(['/usr/sbin/ioreg', 370 | '-c', 'IOPlatformExpertDevice', 371 | '-d', '2', 372 | '-a']) 373 | d = plistlib.readPlistFromString(output) 374 | return d['IORegistryEntryChildren'][0]['IOPlatformSerialNumber'] 375 | 376 | def get_activepower(self): 377 | '''Returns the active power source 378 | Battey or AC''' 379 | output = subprocess.check_output(['/usr/bin/pmset', '-g']) 380 | for line in output.splitlines(): 381 | if '*' in line: 382 | return line.split(' ')[0] 383 | 384 | def get_batterycycles(self): 385 | '''Returns the number of battery cycles''' 386 | output = subprocess.check_output(['/usr/sbin/ioreg', 387 | '-r', 388 | '-c', 'AppleSmartBattery', 389 | '-a']) 390 | if output: 391 | d = plistlib.readPlistFromString(output)[0] 392 | return d['CycleCount'] 393 | 394 | def get_batteryhealth(self): 395 | '''Returns the battery health 396 | Healthy or Failing''' 397 | output = subprocess.check_output(['/usr/sbin/ioreg', 398 | '-r', 399 | '-c', 'AppleSmartBattery', 400 | '-a']) 401 | if output: 402 | d = plistlib.readPlistFromString(output)[0] 403 | if not d['PermanentFailureStatus']: 404 | return 'Healthy' 405 | else: 406 | return 'Failing' 407 | 408 | def get_batteryserial(self): 409 | '''Returns the serial of the battery''' 410 | output = subprocess.check_output(['/usr/sbin/ioreg', 411 | '-r', 412 | '-c', 'AppleSmartBattery', 413 | '-a']) 414 | if output: 415 | d = plistlib.readPlistFromString(output)[0] 416 | return d['BatterySerialNumber'] 417 | 418 | def get_batterycharging(self): 419 | '''Returns the serial of the battery''' 420 | output = subprocess.check_output(['/usr/sbin/ioreg', 421 | '-r', 422 | '-c', 'AppleSmartBattery', 423 | '-a']) 424 | if output: 425 | d = plistlib.readPlistFromString(output)[0] 426 | return d['IsCharging'] 427 | 428 | def get_batterycharged(self): 429 | '''Returns the serial of the battery''' 430 | output = subprocess.check_output(['/usr/sbin/ioreg', 431 | '-r', 432 | '-c', 'AppleSmartBattery', 433 | '-a']) 434 | if output: 435 | d = plistlib.readPlistFromString(output)[0] 436 | return d['FullyCharged'] 437 | 438 | def get_batteryremaining(self): 439 | '''Returns the serial of the battery''' 440 | output = subprocess.check_output(['/usr/sbin/ioreg', 441 | '-r', 442 | '-c', 'AppleSmartBattery', 443 | '-a']) 444 | if output: 445 | d = plistlib.readPlistFromString(output)[0] 446 | return d['TimeRemaining'] 447 | 448 | def get_batterycapacity(self): 449 | '''Returns the serial of the battery''' 450 | output = subprocess.check_output(['/usr/sbin/ioreg', 451 | '-r', 452 | '-c', 'AppleSmartBattery', 453 | '-a']) 454 | if output: 455 | d = plistlib.readPlistFromString(output)[0] 456 | return d['CurrentCapacity'] 457 | 458 | def get_batterypercentage(self): 459 | '''Returns the percetage of the battery''' 460 | if 'Book' in self.get_model(): 461 | task = ['pmset', '-g', 'batt'] 462 | return re.findall(r'\d+%', subprocess.check_output( 463 | task))[0].replace('%','') 464 | 465 | def get_freespace(self): 466 | '''Returns the free space on the boot drive''' 467 | output = subprocess.check_output(['/usr/sbin/diskutil', 468 | 'info', 469 | '-plist', 470 | '/']) 471 | xml = plistlib.readPlistFromString(output) 472 | return int(xml['FreeSpace']) / 1000 / 1000 / 1000 473 | 474 | def get_bootcamp(self): 475 | '''Returns whether a bootcamp volume is detected''' 476 | output = subprocess.check_output(['/usr/sbin/diskutil', 'list']) 477 | for line in output.splitlines(): 478 | if 'Microsoft Basic Data' in line: 479 | return 'Yes' 480 | return 'No' 481 | 482 | def get_filevalutstatus(self): 483 | '''Returns whether filevault is enabled''' 484 | output = subprocess.check_output(['/usr/bin/fdesetup', 'status']) 485 | return output.strip() 486 | 487 | def get_gatekeeperstatus(self): 488 | '''Returns whether gatekeeper is enabled''' 489 | proc = subprocess.Popen(['/usr/sbin/spctl', '--status'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) 490 | stdout, stderr = proc.communicate() 491 | return stdout.strip() 492 | 493 | def get_primaryinterface(self): 494 | '''Returns the active network interface of the Mac''' 495 | # output = subprocess.check_output(['/usr/sbin/netstat', '-rn']) 496 | # for line in output.splitlines(): 497 | # if 'default' in line and not 'utun' in line: 498 | # return line.split(' ')[-1].strip() 499 | try: 500 | states = SCDynamicStoreCopyValue(self.net_config, "State:/Network/Global/IPv4") 501 | return states['PrimaryInterface'] 502 | except TypeError: 503 | return None 504 | 505 | def get_macaddress(self): 506 | '''Returns the MAC address of the current active network''' 507 | interface = self.primaryinterface 508 | if interface: 509 | output = subprocess.check_output(['/usr/sbin/networksetup', 510 | '-getmacaddress', interface]) 511 | return output.split(' ')[2] 512 | 513 | def get_model(self): 514 | '''Returns the hardware model of the Mac''' 515 | output = subprocess.check_output(['/usr/sbin/sysctl', '-n', 516 | 'hw.model']) 517 | return output.strip() 518 | 519 | def get_memory(self): 520 | '''Returns the memory in bytes of the Mac''' 521 | output = subprocess.check_output(['/usr/sbin/sysctl', '-n', 522 | 'hw.memsize']) 523 | return int(output.strip())/1024/1024/1024 524 | 525 | def get_processor(self): 526 | '''Returns the processor model of the Mac''' 527 | output = subprocess.check_output(['/usr/sbin/sysctl', '-n', 528 | 'machdep.cpu.brand_string']) 529 | return output.strip() 530 | 531 | def get_is_64(self): 532 | '''Returns whether the Mac is 64-bit capable''' 533 | output = subprocess.check_output(['/usr/sbin/sysctl', '-n', 534 | 'hw.cpu64bit_capable']) 535 | if output: 536 | return True 537 | return False 538 | 539 | def get_buildversion(self): 540 | '''Returns the os build version of the Mac''' 541 | output = subprocess.check_output(['/usr/sbin/sysctl', '-n', 542 | 'kern.osversion']) 543 | return output.strip() 544 | 545 | def get_cpucores(self): 546 | '''Returns how many CPU cores on the Mac''' 547 | output = subprocess.check_output(['/usr/sbin/sysctl', '-n', 'hw.ncpu']) 548 | return output.strip() 549 | 550 | def get_cpus(self): 551 | '''Returns how many CPUs on the Mac''' 552 | output = subprocess.check_output(['/usr/sbin/sysctl', '-n', 553 | 'hw.physicalcpu']) 554 | return output.strip() 555 | 556 | def get_uuid(self): 557 | '''Returns the UUID of the Mac''' 558 | output = subprocess.check_output(['/usr/sbin/sysctl', '-n', 559 | 'kern.uuid']) 560 | return output.strip() 561 | 562 | def get_screenresolution(self): 563 | '''Returns the screen resolution of the main screen''' 564 | width = NSScreen.mainScreen().frame().size.width 565 | height = NSScreen.mainScreen().frame().size.height 566 | return width, height 567 | 568 | def get_sus(self): 569 | '''Returns the custom SUS if set''' 570 | sus = CFPreferencesCopyAppValue('CatalogURL', 571 | '/Library/Preferences/com.apple.SoftwareUpdate.plist') 572 | return sus 573 | 574 | def get_javaversion(self): 575 | '''Returns the java plug-in version''' 576 | plist = '/Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Info.plist' 577 | if os.path.exists(plist): 578 | return CFPreferencesCopyAppValue('CFBundleVersion', plist) 579 | 580 | def get_javavendor(self): 581 | '''Returns the java vendor (apple or oracle)''' 582 | plist = '/Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Info.plist' 583 | if os.path.exists(plist): 584 | return CFPreferencesCopyAppValue('CFBundleIdentifier', plist).split('.')[1] 585 | 586 | def get_remotelogin(self): 587 | '''Returns whether remote login is activated''' 588 | if os.geteuid() == 0: 589 | output = subprocess.check_output(['/usr/sbin/systemsetup', 590 | 'getremotelogin']) 591 | return output.split(': ')[1].strip() 592 | return "run as root to check this setting" 593 | 594 | def get_is_virtual(self): 595 | '''Returns whether the Mac is a virtual machine''' 596 | output = subprocess.check_output(['/usr/sbin/sysctl', '-n', 597 | 'machdep.cpu.features']) 598 | if 'VMM' in output: 599 | return True 600 | return False 601 | 602 | def get_board_id(self): 603 | '''Returns the board-id of the Mac 604 | Ref: https://github.com/hjuutilainen/adminscripts/blob/master/check-mavericks-compatibility.py''' 605 | output = subprocess.check_output(['/usr/sbin/ioreg', 606 | '-p', 'IODeviceTree', 607 | '-r', 608 | '-n', '/', 609 | '-d', '1', 610 | '-a']) 611 | d = plistlib.readPlistFromString(output) 612 | return str(d[0]['board-id']).split("'")[1].split('\\')[0] 613 | 614 | def get_drive_model(self): 615 | '''Returns drive model 616 | Ref: https://github.com/hjuutilainen/adminscripts/blob/master/check-mavericks-compatibility.py''' 617 | output = subprocess.check_output(['/usr/sbin/ioreg', 618 | '-p', 'IOService', 619 | '-n', 'AppleAHCIDiskDriver', 620 | '-r', 621 | '-l', 622 | '-d', '1', 623 | '-w', '0', 624 | '-a']) 625 | d = plistlib.readPlistFromString(output) 626 | return d[0]['Model'].strip() 627 | 628 | def get_drive_revision(self): 629 | '''Returns drive revision 630 | Ref: https://github.com/hjuutilainen/adminscripts/blob/master/check-mavericks-compatibility.py''' 631 | output = subprocess.check_output(['/usr/sbin/ioreg', 632 | '-p', 'IOService', 633 | '-n', 'AppleAHCIDiskDriver', 634 | '-r', 635 | '-l', 636 | '-d', '1', 637 | '-w', '0', 638 | '-a']) 639 | d = plistlib.readPlistFromString(output) 640 | return d[0]['Revision'].strip() 641 | 642 | def get_drive_serial(self): 643 | '''Returns a tuple of the firmware version of the Mac 644 | Ref: https://github.com/hjuutilainen/adminscripts/blob/master/check-mavericks-compatibility.py''' 645 | output = subprocess.check_output(['/usr/sbin/ioreg', 646 | '-p', 'IOService', 647 | '-n', 'AppleAHCIDiskDriver', 648 | '-r', 649 | '-l', 650 | '-d', '1', 651 | '-w', '0', 652 | '-a']) 653 | d = plistlib.readPlistFromString(output) 654 | return d[0]['Serial Number'].strip() 655 | 656 | def get_cidr(self): 657 | '''Returns the cidr of the current network''' 658 | netmask = '' 659 | ip = self.get_ip() 660 | if ip: 661 | output = subprocess.check_output(['/sbin/ifconfig', 'en0']) 662 | for line in output.splitlines(): 663 | if ip in line: 664 | netmask = line.split(' ')[3].lower() 665 | count = int(0) 666 | count+=int(netmask.count('f')) * 4 667 | count+=int(netmask.count('e')) * 3 668 | count+=int(netmask.count('c')) * 2 669 | count+=int(netmask.count('8')) * 1 670 | return '%s.%s/%s' % ('.'.join(ip.split('.')[0:3]), '0', count) 671 | 672 | def get_bootvolume(self): 673 | '''Returns the boot volume name''' 674 | output = subprocess.check_output(['/usr/sbin/diskutil', 'info', 675 | '-plist', '/']) 676 | xml = plistlib.readPlistFromString(output) 677 | return xml['VolumeName'] 678 | 679 | def get_recoveryhd(self): 680 | '''Returns the recovery hd device name''' 681 | output = subprocess.check_output(['/usr/sbin/diskutil', 'info', 682 | '-plist', '/']) 683 | xml = plistlib.readPlistFromString(output) 684 | return xml['RecoveryDeviceIdentifier'] 685 | 686 | def get_volumeuuid(self): 687 | '''Returns the boot volume uuid''' 688 | output = subprocess.check_output(['/usr/sbin/diskutil', 'info', 689 | '-plist', '/']) 690 | xml = plistlib.readPlistFromString(output) 691 | return xml['VolumeUUID'] 692 | 693 | def get_boottime(self): 694 | '''Returns the system uptime''' 695 | cmd = ['sysctl', '-n', 'kern.boottime'] 696 | task = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 697 | (epoch, err) = task.communicate() 698 | return ".".join([datetime.datetime.fromtimestamp(int(epoch.split(' = ')[1].split(',')[0])).strftime('%Y-%m-%d %H:%M:%S'), '000']) 699 | 700 | def get_is_laptop(self): 701 | '''Returns whether the Mac is a laptop''' 702 | return True if 'book' in self.hardwaredatatype['machine_model'].lower() else False 703 | 704 | def get_smc_version(self): 705 | '''Returns the smc version''' 706 | return self.hardwaredatatype['SMC_version_system'] 707 | 708 | def get_boot_rom(self): 709 | '''Returns the boot rom version''' 710 | return self.hardwaredatatype['boot_rom_version'] 711 | 712 | def get_activeinterfaces(self): 713 | interfaces = [ SCNetworkInterfaceGetBSDName(i) for i in SCNetworkInterfaceCopyAll() if SCNetworkInterfaceGetBSDName(i).startswith('en') ] 714 | d = {} 715 | for i in interfaces: 716 | try: 717 | d[i] = subprocess.check_output(['/usr/sbin/ipconfig', 'getifaddr', i]).strip() 718 | except subprocess.CalledProcessError: 719 | continue 720 | return d 721 | 722 | def get_password_changed(self, username=None): 723 | '''Gets the date of last password change''' 724 | # for 10.10+ or non-migrated accounts 725 | username = SCDynamicStoreCopyConsoleUser(None, None, None)[0] 726 | task = subprocess.check_output(['/usr/bin/dscl', '.', 'read', 'Users/' + username, 'accountPolicyData']) 727 | plist = plistlib.readPlistFromString('\n'.join(task.split()[1:])) 728 | if 'creationTime' in plist.keys(): 729 | creation_date = datetime.datetime.utcfromtimestamp(plist['creationTime']).date() 730 | if 'passwordLastSetTime' in plist.keys(): 731 | return datetime.datetime.utcfromtimestamp(plist['passwordLastSetTime']).date() 732 | else: 733 | # for 10.9.x and lower, or migrated accounts 734 | task = subprocess.Popen(['/usr/bin/dscl', '.', 'read', 'Users/' + username, 'PasswordPolicyOptions'], 735 | stdout=subprocess.PIPE, 736 | stderr=subprocess.PIPE) 737 | (out, err) = task.communicate() 738 | if not err: 739 | plist = plistlib.readPlistFromString('\n'.join(out.split()[1:])) 740 | if 'passwordLastSetTime' in plist.keys(): 741 | return plist['passwordLastSetTime'].date() 742 | return creation_date 743 | 744 | def get_password_age(self): 745 | '''Gets the age (in days) since the last password change''' 746 | username = SCDynamicStoreCopyConsoleUser(None, None, None)[0] 747 | if username: 748 | changed = self.get_password_changed(username) 749 | if changed: 750 | today = datetime.datetime.utcnow().date() 751 | pw_age = (today - changed).days 752 | return pw_age 753 | else: 754 | return 'Undetermined' 755 | 756 | def get_printers(self): 757 | '''Returns name, ppd, and uri of printers 758 | ref: https://gist.githubusercontent.com/arubdesu/639d3d228824ebc57cc0/raw/fdbef07ded37c8ed8d2c2973e6fa3ede505d0ea2/ad-hoc_inventory.py''' 759 | full_prints = plistlib.readPlistFromString( 760 | subprocess.check_output( 761 | ['/usr/sbin/system_profiler', 762 | 'SPPrintersDataType', 763 | '-xml'])) 764 | all_printdicts = full_prints[0]['_items'] 765 | just_prints = {} 766 | for printer in all_printdicts: 767 | just_prints[printer.get('_name', None)] = [printer.get('ppd', None), 768 | printer.get('uri', None)] 769 | return just_prints 770 | 771 | def get_adinfo(self): 772 | '''Returns the ad directory info''' 773 | ad_info = SCDynamicStoreCopyValue(self.net_config, "com.apple.opendirectoryd.ActiveDirectory") 774 | return ad_info 775 | 776 | def get_dscontactsnode(self): 777 | '''Returns the directory /Contacts node''' 778 | contacts_node = SCDynamicStoreCopyValue(self.net_config, "com.apple.opendirectoryd.node:/Contacts") 779 | return contacts_node[0] 780 | 781 | def get_dssearchnode(self): 782 | '''Returns the directory /Search node''' 783 | l = [] 784 | search_node = SCDynamicStoreCopyValue(self.net_config, "com.apple.opendirectoryd.node:/Contacts") 785 | return search_node[0] 786 | 787 | def main(): 788 | '''Runs in interactive mode''' 789 | parser = argparse.ArgumentParser() 790 | parser.add_argument('-l', '--list', help='list categories', 791 | action='store_true') 792 | parser.add_argument('category', help='input category', 793 | nargs=argparse.REMAINDER) 794 | 795 | args = parser.parse_args() 796 | 797 | facts = Facts() 798 | l = [] 799 | for k, v in Facts.__dict__.items(): 800 | if callable(v): 801 | l.append(k.replace('get_', '')) 802 | l.remove('__init__') 803 | 804 | if args.list: 805 | for i in sorted(l): 806 | print i 807 | elif args.category: 808 | try: 809 | print getattr(facts, 'get_' + args.category[0])() 810 | except AttributeError as err: 811 | print err 812 | exit(1) 813 | else: 814 | d = {} 815 | for i in sorted(l): 816 | print 'Processing: %s' % i 817 | d[i] = getattr(facts, 'get_' + i)() 818 | for k, v in sorted(d.items()): 819 | print '%s => %s' % (k, v) 820 | 821 | if __name__ == "__main__": 822 | main() 823 | -------------------------------------------------------------------------------- /pyioreg-objc.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | #ref https://gist.github.com/pudquick/c7dd1262bd81a32663f0 3 | 4 | import objc 5 | from Foundation import NSBundle 6 | 7 | IOKit_bundle = NSBundle.bundleWithIdentifier_('com.apple.framework.IOKit') 8 | 9 | functions = [("IOServiceGetMatchingService", b"II@"), 10 | ("IOServiceMatching", b"@*"), 11 | ("IORegistryEntryCreateCFProperty", b"@I@@I"), 12 | ] 13 | 14 | objc.loadBundleFunctions(IOKit_bundle, globals(), functions) 15 | 16 | def io_key(keyname): 17 | return IORegistryEntryCreateCFProperty(IOServiceGetMatchingService(0, IOServiceMatching("IOPlatformExpertDevice")), keyname, None, 0) 18 | 19 | def battery_info(keyname): 20 | return IORegistryEntryCreateCFProperty(IOServiceGetMatchingService(0, IOServiceMatching("AppleSmartBattery")), keyname, None, 0) 21 | 22 | def get_hardware_uuid(): 23 | return io_key("IOPlatformUUID") 24 | 25 | def get_hardware_serial(): 26 | return io_key("IOPlatformSerialNumber") 27 | 28 | def get_battery_charge(): 29 | return battery_info("FullyCharged") 30 | 31 | def get_battery_capacity(): 32 | return battery_info("CurrentCapacity") 33 | 34 | print get_hardware_uuid() 35 | print get_hardware_serial() 36 | print get_battery_charge() 37 | print get_battery_capacity() 38 | -------------------------------------------------------------------------------- /pyioreg.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import os 4 | import sys 5 | import subprocess 6 | import plistlib 7 | 8 | def get_ioreg(): 9 | '''Returns a dict of all ioreg data''' 10 | cmd = ['/usr/sbin/ioreg', '-c', 'IOPlatformExpertDevice', '-d', '2', '-a'] 11 | task = subprocess.Popen(cmd, stdout=subprocess.PIPE, 12 | stderr=subprocess.PIPE) 13 | (out, err) = task.communicate() 14 | plist = plistlib.readPlistFromString(out) 15 | return plist 16 | 17 | def main(): 18 | d = get_ioreg() 19 | # print d['IOPlatformSerialNumber'] 20 | # print type(d['IORegistryEntryChildren']) 21 | print len(d['IORegistryEntryChildren']) 22 | for k, v in d['IORegistryEntryChildren'][0].items(): 23 | print k, v 24 | print d['IORegistryEntryChildren'][0]['IOPlatformSerialNumber'] 25 | 26 | if __name__ == "__main__": 27 | main() 28 | -------------------------------------------------------------------------------- /pynotify.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | ############################################################################## 4 | # Copyright 2015 Joseph Chilcote 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); you may not 7 | # use this file except in compliance with the License. You may obtain a copy 8 | # of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 14 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 15 | # License for the specific language governing permissions and limitations 16 | # under the License. 17 | ############################################################################## 18 | 19 | __author__ = 'Joseph Chilcote (chilcote@gmail.com)' 20 | __version__ = '0.0.1' 21 | 22 | from Foundation import NSUserNotification 23 | from Foundation import NSUserNotificationCenter 24 | from Foundation import NSUserNotificationDefaultSoundName 25 | from Foundation import NSDate 26 | import sys 27 | import argparse 28 | 29 | class Notify(object): 30 | '''Class to send custom notification''' 31 | def __init__(self): 32 | self.notification = NSUserNotification.alloc().init() 33 | 34 | def alert(self, title, subtitle, message, delay=0, sound=False, userInfo={}): 35 | self.notification.setTitle_(title) 36 | self.notification.setSubtitle_(subtitle) 37 | self.notification.setInformativeText_(message) 38 | self.notification.setDeliveryDate_(NSDate.dateWithTimeInterval_sinceDate_(delay, NSDate.date())) 39 | # self.notification.setUserInfo_(userInfo) 40 | if sound: 41 | self.notification.setSoundName_("NSUserNotificationDefaultSoundName") 42 | NSUserNotificationCenter.defaultUserNotificationCenter().scheduleNotification_(self.notification) 43 | 44 | def main(): 45 | '''Main method''' 46 | parser = argparse.ArgumentParser(description='Send a custom notification on OS X.') 47 | parser.add_argument('--title', help='title of notification') 48 | parser.add_argument('--subtitle', help='subtitle of notification') 49 | parser.add_argument('--message', help='message of notification') 50 | parser.add_argument('--delay', type=float, help='delay for n seconds') 51 | parser.add_argument('--sound', help='include audible alert', action='store_true') 52 | args = parser.parse_args() 53 | 54 | title = args.title if args.title else None 55 | subtitle = args.subtitle if args.title else None 56 | message = args.message if args.title else None 57 | delay = args.delay if args.delay else 0 58 | sound = True if args.sound else False 59 | 60 | notify = Notify() 61 | notify.alert(title, subtitle, message, delay, sound=sound) 62 | 63 | if __name__ == '__main__': 64 | main() 65 | -------------------------------------------------------------------------------- /pypkgutil.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | ''' 4 | This script collects data from pkgutil --pkgs 5 | Yes, everything in this script can be done in the shell with the pkgutil 6 | command, but I wanted to play with python. Sue me. 7 | Inspiration came from a twitter from bruienne linking to this gist: 8 | https://gist.github.com/bruienne/3fd12e07421ac875e747 9 | ''' 10 | 11 | ############################################################################## 12 | # Copyright 2014 Joseph Chilcote 13 | # 14 | # Licensed under the Apache License, Version 2.0 (the "License"); you may not 15 | # use this file except in compliance with the License. You may obtain a copy 16 | # of the License at 17 | # 18 | # http://www.apache.org/licenses/LICENSE-2.0 19 | # 20 | # Unless required by applicable law or agreed to in writing, software 21 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 22 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 23 | # License for the specific language governing permissions and limitations 24 | # under the License. 25 | ############################################################################## 26 | 27 | ''' 28 | todo: 29 | add a search function 30 | add a forget function 31 | add a function to pull plist of specified pkgs 32 | 33 | ''' 34 | 35 | import os, sys, subprocess 36 | import plistlib 37 | 38 | 39 | def get_data(): 40 | l = [] 41 | cmd = ['pkgutil', '--pkgs'] 42 | task = subprocess.Popen(cmd, stdout=subprocess.PIPE, 43 | stderr=subprocess.PIPE) 44 | (out, err) = task.communicate() 45 | for line in out.splitlines(): 46 | l.append(line) 47 | return l 48 | 49 | def usage(): 50 | return './pypkgutil.py [KEY]' 51 | 52 | def main(): 53 | pkgs = get_data() 54 | if len(sys.argv) == 1: 55 | for i in pkgs: 56 | print i 57 | elif len(sys.argv) == 2: 58 | for i in pkgs: 59 | if sys.argv[1].lower() in i.lower(): 60 | print i 61 | else: 62 | print usage() 63 | sys.exit(0) 64 | 65 | if __name__ == "__main__": 66 | main() 67 | 68 | -------------------------------------------------------------------------------- /pypsutil.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | import psutil 3 | import os, sys, time 4 | 5 | pid = os.getpid() 6 | p = psutil.Process(pid) 7 | print('Process info:') 8 | print(' name :', p.name()) 9 | print(' exe :', p.exe()) 10 | 11 | data = [] 12 | while True: 13 | data += list(range(100000)) 14 | info = p.memory_full_info() 15 | # Convert to MB 16 | memory = info.uss / 1024 / 1024 17 | print('Memory used: {:.2f} MB'.format(memory)) 18 | if memory > 40: 19 | print('Memory too big! Exiting.') 20 | sys.exit() 21 | time.sleep(1) 22 | -------------------------------------------------------------------------------- /pyscreenrez.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """ 4 | pyscreenrez.py 5 | 6 | Reference: 7 | https://gist.github.com/gregneagle/5722568 8 | https://github.com/Tonyliu2ca/ScreenResolution/blob/master/screenresolution.m 9 | 10 | Apple Docs: 11 | https://developer.apple.com/library/mac/#documentation/graphicsimaging/reference/Quartz_Services_Ref/Reference/reference.html 12 | https://developer.apple.com/library/mac/#documentation/graphicsimaging/Conceptual/QuartzDisplayServicesConceptual/Introduction/Introduction.html#//apple_ref/doc/uid/TP40004316 13 | """ 14 | 15 | import sys, os 16 | import Quartz 17 | 18 | mode = '' 19 | 20 | def get_screen_resolution(): 21 | mainMonitor = Quartz.CGDisplayBounds(Quartz.CGMainDisplayID()) 22 | return (mainMonitor.size.width, mainMonitor.size.height) 23 | 24 | def get_main_display(): 25 | return Quartz.CGMainDisplayID() 26 | 27 | def get_active_displays(): 28 | max_displays = 2 29 | (err, active_displays, number_of_active_displays) = Quartz.CGGetActiveDisplayList(max_displays, None, None) 30 | return active_displays 31 | 32 | def get_display_modes(display): 33 | return Quartz.CGDisplayCopyAllDisplayModes(display, None) 34 | 35 | def set_screen_resolution(mode, display): 36 | (err, config_ref) = Quartz.CGBeginDisplayConfiguration(None) 37 | if err: 38 | print >> sys.stderr, "Error with CGBeginDisplayConfiguration: %s" % err 39 | exit(-1) 40 | 41 | # this is the part I need to figure out 42 | # Need to pass the correct mode (1024 x 768 @ 60) 43 | err = Quartz.CGConfigureDisplayWithDisplayMode(config_ref, mode, display, None) 44 | if err: 45 | print >> sys.stderr, "Error with CGConfigureDisplayWithDisplayMode: %s" % err 46 | exit(-1) 47 | 48 | err = Quartz.CGCompleteDisplayConfiguration(config_ref, Quartz.kCGConfigurePermanently) 49 | if err: 50 | print >> sys.stderr, ("Error with CGCompleteDisplayConfiguration: %s" % err) 51 | exit(-1) 52 | 53 | def main(): 54 | screensize = get_screen_resolution() 55 | display = get_main_display() 56 | active_displays = get_active_displays() 57 | mode_list = get_display_modes(display) 58 | 59 | print 'Main Display: %s' % display 60 | print 'Active Displays: %s' % active_displays[0] 61 | print 'Resolution: %s, %s' % screensize 62 | 63 | # for mode in mode_list: 64 | # print mode 65 | 66 | # set_screen_resolution(mode, display) 67 | 68 | if __name__ == '__main__': 69 | main() -------------------------------------------------------------------------------- /pysystemprofiler.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | ''' 4 | This script collects data from system_profiler 5 | ''' 6 | 7 | ############################################################################## 8 | # Copyright 2014 Joseph Chilcote 9 | # 10 | # Licensed under the Apache License, Version 2.0 (the "License"); you may not 11 | # use this file except in compliance with the License. You may obtain a copy 12 | # of the License at 13 | # 14 | # http://www.apache.org/licenses/LICENSE-2.0 15 | # 16 | # Unless required by applicable law or agreed to in writing, software 17 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 18 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 19 | # License for the specific language governing permissions and limitations 20 | # under the License. 21 | ############################################################################## 22 | 23 | import os, sys, subprocess 24 | import plistlib 25 | from pprint import pprint 26 | 27 | class SystemProfiler(object): 28 | '''This object returns dictionaries of data from system_profiler''' 29 | 30 | def __init__(self): 31 | pass 32 | 33 | def get_data(self, category): 34 | '''This method is called by all the get methods to gather 35 | category specific system_profiler data''' 36 | cmd = '/usr/sbin/system_profiler', category, '-xml' 37 | task = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 38 | (out, err) = task.communicate() 39 | plist = plistlib.readPlistFromString(out)[0] 40 | return plist 41 | 42 | def get_SPStorageDataType(self): 43 | '''Returns a dict containing storage device data''' 44 | d = self.get_data('SPStorageDataType')['_items'] 45 | if 'com.apple.corestorage.lvg' in d: 46 | return d['_items']['com.apple.corestorage.lvg'][0] 47 | return d[0] 48 | 49 | def get_SPHardwareDataType(self): 50 | '''Returns a dict containing hardware data''' 51 | d = self.get_data('SPHardwareDataType')['_items'] 52 | return d[0] 53 | 54 | def get_SPUniversalAccessDataType(self): 55 | '''Returns a dict containing universal access data''' 56 | d = self.get_data('SPUniversalAccessDataType')['_items'] 57 | return d[0] 58 | 59 | def get_SPDeveloperToolsDataType(self): 60 | '''Returns a dict containing dev tools data''' 61 | d = self.get_data('SPDeveloperToolsDataType')['_items'] 62 | return d[0] 63 | 64 | def get_SPConfigurationProfileDataType(self): 65 | '''Returns a dict containing configuration profile data''' 66 | d = self.get_data('SPConfigurationProfileDataType') 67 | print type(d) 68 | if d['_items']: 69 | return d['_items'][0]['_items'][0] 70 | return {} 71 | 72 | def get_SPInstallHistoryDataType(self): 73 | '''Returns a dict containing install history data''' 74 | d = self.get_data('SPInstallHistoryDataType')['_items'] 75 | d1 = {} 76 | for item in d: 77 | d1[item['_name']] = item['install_date'] 78 | return d1 79 | 80 | def get_SPDiagnosticsDataType(self): 81 | '''Returns a dict containing diagnostic data''' 82 | d = self.get_data('SPDiagnosticsDataType')['_items'] 83 | return d[0] 84 | 85 | def get_SPFirewallDataType(self): 86 | '''Returns a dict containing firewall data''' 87 | d = self.get_data('SPFirewallDataType')['_items'] 88 | return d[0] 89 | 90 | def get_SPDisplaysDataType(self): 91 | '''Returns a dict containing display data''' 92 | d = self.get_data('SPDisplaysDataType')['_items'] 93 | return d[0] 94 | 95 | def get_SPNetworkLocationDataType(self): 96 | '''Returns a dict containing network location data''' 97 | d = self.get_data('SPNetworkLocationDataType')['_items'] 98 | return d[0]['spnetworklocation_services'][0] 99 | 100 | def SPManagedClientDataType(self): 101 | '''Returns a dict containing managed client data''' 102 | d = self.get_data('SPManagedClientDataType')['_items'] 103 | if d: 104 | return d[0]['_items'][0] 105 | return {} 106 | 107 | def get_SPMemoryDataType(self): 108 | '''Returns a list of dicts containing memory module data''' 109 | d = self.get_data('SPMemoryDataType')['_items'] 110 | l = [] 111 | for item in d[0]['_items']: 112 | l.append(item) 113 | return l 114 | 115 | def get_SPNetworkDataType(self): 116 | '''Returns a list of dicts containing network interface data''' 117 | d = self.get_data('SPNetworkDataType')['_items'] 118 | l = [] 119 | for item in d: 120 | l.append(item) 121 | return l 122 | 123 | def get_SPPowerDataType(self): 124 | '''Returns a dict containing power data''' 125 | d = self.get_data('SPPowerDataType')['_items'] 126 | d1 = {} 127 | for item in d: 128 | d1.update(item) 129 | return d1 130 | 131 | def get_SPPrefPaneDataType(self): 132 | '''Returns a list of dicts containing pref pane data''' 133 | d = self.get_data('SPPrefPaneDataType')['_items'] 134 | l = [] 135 | for item in d: 136 | l.append(item) 137 | return l 138 | 139 | def get_SPPrintersDataType(self): 140 | '''Returns a list of dicts containing printer data''' 141 | d = self.get_data('SPPrintersDataType')['_items'] 142 | l = [] 143 | for item in d: 144 | l.append(item) 145 | return l 146 | 147 | def get_SPSerialATADataType(self): 148 | '''Returns a dict containing sata data''' 149 | d = self.get_data('SPSerialATADataType')['_items'] 150 | return d[0]['_items'][0] 151 | 152 | def get_SPSoftwareDataType(self): 153 | '''Returns a dict containing system software data''' 154 | d = self.get_data('SPSoftwareDataType')['_items'] 155 | return d[0] 156 | 157 | def get_SPNetworkVolumeDataType(self): 158 | '''Returns a list of dicts network volume data''' 159 | d = self.get_data('SPNetworkVolumeDataType')['_items'] 160 | l = [] 161 | for item in d: 162 | l.append(item) 163 | return l 164 | 165 | def get_SPAirPortDataType(self): 166 | '''Returns a dict containing airport data''' 167 | d = self.get_data('SPAirPortDataType')['_items'] 168 | d1 = {} 169 | for item in d[0]['spairport_airport_interfaces']: 170 | d1.update(item) 171 | d1.update(d[0]['spairport_software_information']) 172 | return d1 173 | 174 | def print_data(title, dct): 175 | print '\n%s\n-------------------' % title 176 | for k, v in dct.items(): 177 | print '%s: %s' % (k,v) 178 | 179 | def main(): 180 | sysprofiler = SystemProfiler() 181 | 182 | storage_data = sysprofiler.get_SPStorageDataType() 183 | print_data('STORAGE DATA', storage_data) 184 | hardware_data = sysprofiler.get_SPHardwareDataType() 185 | print_data('HARDWARE DATA', hardware_data) 186 | universal_access = sysprofiler.get_SPUniversalAccessDataType() 187 | print_data('UNIVERSAL ACCESS', universal_access) 188 | dev_tools = sysprofiler.get_SPDeveloperToolsDataType() 189 | print_data('DEVELOPER TOOLS', dev_tools) 190 | config_profile = sysprofiler.get_SPConfigurationProfileDataType() 191 | print_data('CONFIGURATION PROFILE', config_profile) 192 | install_history = sysprofiler.get_SPInstallHistoryDataType() 193 | print_data('INSTALL HISTORY', install_history) 194 | diagnostic_data = sysprofiler.get_SPDiagnosticsDataType() 195 | print_data('DIAGNOSTIC DATA', diagnostic_data) 196 | firewall_settings = sysprofiler.get_SPFirewallDataType() 197 | print_data('FIREWALL SETTINGS', firewall_settings) 198 | display_data = sysprofiler.get_SPDisplaysDataType() 199 | print_data('DISPLAY DATA', display_data) 200 | network_location = sysprofiler.get_SPNetworkLocationDataType() 201 | print_data('NETWORK LOCATIONS', network_location) 202 | managed_client_data = sysprofiler.SPManagedClientDataType() 203 | print_data('MANAGED CLIENT DATA', managed_client_data) 204 | memory_data = sysprofiler.get_SPMemoryDataType() 205 | for item in memory_data: 206 | print_data('MEMORY DATA', item) 207 | network_data = sysprofiler.get_SPNetworkDataType() 208 | for item in network_data: 209 | print_data('NETWORK DATA', item) 210 | power_data = sysprofiler.get_SPPowerDataType() 211 | print_data('POWER DATA', power_data) 212 | pref_data = sysprofiler.get_SPPrefPaneDataType() 213 | for item in pref_data: 214 | print_data('PREFERENCE PANE DATA', item) 215 | printer_data = sysprofiler.get_SPPrintersDataType() 216 | for item in printer_data: 217 | print_data('PRINTER DATA', item) 218 | sata_data = sysprofiler.get_SPSerialATADataType() 219 | print_data('SATA DATA', sata_data) 220 | software_data = sysprofiler.get_SPSoftwareDataType() 221 | print_data('SOFTWARE DATA', software_data) 222 | network_volume_data = sysprofiler.get_SPNetworkVolumeDataType() 223 | for item in network_volume_data: 224 | print_data('NETWORK VOLUME DATA', item) 225 | airport_data = sysprofiler.get_SPAirPortDataType() 226 | print_data('AIRPORT DATA', airport_data) 227 | 228 | if __name__ == "__main__": 229 | main() -------------------------------------------------------------------------------- /pyvm.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | #ref https://gist.github.com/pudquick/8107bb7b6e8d63eaddec7042c081e656 3 | 4 | from ctypes import CDLL, c_uint, byref, create_string_buffer 5 | libc = CDLL('/usr/lib/libc.dylib') 6 | 7 | def sysctl(name): 8 | size = c_uint(0) 9 | libc.sysctlbyname(name, None, byref(size), None, 0) 10 | buf = create_string_buffer(size.value) 11 | libc.sysctlbyname(name, buf, byref(size), None, 0) 12 | return buf.value 13 | 14 | def is_mac_vm(): 15 | return 'VMM' in sysctl('machdep.cpu.features').split(' ') 16 | 17 | print is_mac_vm() 18 | print sysctl('machdep.cpu.features') 19 | print sysctl('kern.boottime').split() 20 | print sysctl('hw.model') 21 | print sysctl('kern.osversion') 22 | print sysctl('kern.hostname') 23 | -------------------------------------------------------------------------------- /pywifi.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | ''' 4 | This script returns values regarding the current wi-fi connection 5 | 6 | Documentation: 7 | https://developer.apple.com/library/mac/documentation/CoreWLAN/Reference/CWInterface_reference/translated_content/CWInterface.html 8 | 9 | objc framework import referenced from: 10 | http://stackoverflow.com/questions/12136817/how-do-you-use-pyobjc-to-turn-off-and-on-the-wireless-interfaces-of-a-mac 11 | ''' 12 | 13 | ############################################################################## 14 | # Copyright 2014 Joseph Chilcote 15 | # 16 | # Licensed under the Apache License, Version 2.0 (the "License"); you may not 17 | # use this file except in compliance with the License. You may obtain a copy 18 | # of the License at 19 | # 20 | # http://www.apache.org/licenses/LICENSE-2.0 21 | # 22 | # Unless required by applicable law or agreed to in writing, software 23 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 24 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 25 | # License for the specific language governing permissions and limitations 26 | # under the License. 27 | ############################################################################## 28 | 29 | import objc 30 | 31 | objc.loadBundle('CoreWLAN', 32 | bundle_path='/System/Library/Frameworks/CoreWLAN.framework', 33 | module_globals=globals()) 34 | 35 | class WiFi(object): 36 | ''' WiFI object''' 37 | 38 | def __init__(self): 39 | self.wifi = CWInterface.interfaceNames() 40 | for iname in self.wifi: 41 | self.interface = CWInterface.interfaceWithName_(iname) 42 | self.ip_monitor = self.interface.ipMonitor() 43 | self.last_network_joined = self.interface.lastNetworkJoined() 44 | 45 | def get_wifistatus(self): 46 | if self.interface.powerOn() == 1: 47 | return "Yes" 48 | return "No" 49 | 50 | def get_ssid(self): 51 | return self.interface.ssid() 52 | 53 | def get_interface(self): 54 | return self.interface.interfaceName() 55 | 56 | def get_hardwareaddress(self): 57 | return self.interface.hardwareAddress() 58 | 59 | def get_aggregatenoise(self): 60 | return self.interface.aggregateNoise() 61 | 62 | def get_aggregaterssi(self): 63 | return self.interface.aggregateRSSI() 64 | 65 | def get_bssid(self): 66 | return self.interface.bssid() 67 | 68 | def get_channel(self): 69 | return self.interface.channel() 70 | 71 | def get_transmitrate(self): 72 | return self.interface.transmitRate() 73 | 74 | def get_mcsindex(self): 75 | return self.interface.mcsIndex() 76 | 77 | def get_security(self): 78 | return self.interface.securityMode() 79 | 80 | def get_ipaddress(self): 81 | return self.ip_monitor.ipv4Addresses()[0] 82 | 83 | def get_last_network_joined(self): 84 | return self.last_network_joined.ssid() 85 | 86 | def get_last_tethered_device(self): 87 | return self.interface.lastTetherDeviceJoined() 88 | 89 | def main(): 90 | wifi = WiFi() 91 | print 'Interface: %s' % wifi.get_interface() 92 | print 'Hardware Address: %s' % wifi.get_hardwareaddress() 93 | print 'Active: %s' % wifi.get_wifistatus() 94 | print 'SSID: %s' % wifi.get_ssid() 95 | print 'Aggregate Noise: %s' % wifi.get_aggregatenoise() 96 | print 'BSSID: %s' % wifi.get_bssid() 97 | print 'RSSI: %s' % wifi.get_aggregaterssi() 98 | print 'Channel: %s' % wifi.get_channel() 99 | print 'Transmit Rate: %s' % wifi.get_transmitrate() 100 | print 'MCS Index: %s' % wifi.get_mcsindex() 101 | print 'Security: %s' % wifi.get_security() 102 | print 'IP Address: %s' % wifi.get_ipaddress() 103 | print 'Last Network Joined: %s' % wifi.get_last_network_joined() 104 | print 'Last Tethered Device Joined: %s' % wifi.get_last_tethered_device() 105 | 106 | if __name__ == "__main__": 107 | main() 108 | -------------------------------------------------------------------------------- /re.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # example using re.compile 3 | 4 | import re 5 | 6 | phoneNumRegex = re.compile(r'(\d\d\d)-(\d\d\d-\d\d\d\d)') 7 | mo = phoneNumRegex.search('My number is 206-555-1212') 8 | print mo.groups() 9 | -------------------------------------------------------------------------------- /renew_dhcp_lease.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # from salajander in slack 3 | 4 | from SystemConfiguration import SCDynamicStoreCreate,\ 5 | SCDynamicStoreCopyValue,\ 6 | SCNetworkInterfaceCopyAll,\ 7 | SCNetworkInterfaceGetBSDName,\ 8 | SCNetworkInterfaceForceConfigurationRefresh 9 | 10 | net_config = SCDynamicStoreCreate(None, "net", None, None) 11 | 12 | def update_dhcp(interface): 13 | interfaces = SCNetworkInterfaceCopyAll() 14 | for i in interfaces: 15 | if SCNetworkInterfaceGetBSDName(i) == interface: 16 | return SCNetworkInterfaceForceConfigurationRefresh(i) 17 | return False 18 | 19 | def get_primaryinterface(): 20 | '''Returns the active network interface of the Mac''' 21 | try: 22 | states = SCDynamicStoreCopyValue(net_config, "State:/Network/Global/IPv4") 23 | return states['PrimaryInterface'] 24 | except TypeError: 25 | return None 26 | 27 | interface = get_primaryinterface() 28 | update_dhcp(interface) 29 | -------------------------------------------------------------------------------- /serial_uuid.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | #ref: https://gist.github.com/pudquick/c7dd1262bd81a32663f0 3 | 4 | import objc 5 | from Foundation import NSBundle 6 | 7 | IOKit_bundle = NSBundle.bundleWithIdentifier_('com.apple.framework.IOKit') 8 | 9 | functions = [("IOServiceGetMatchingService", b"II@"), 10 | ("IOServiceMatching", b"@*"), 11 | ("IORegistryEntryCreateCFProperty", b"@I@@I"), 12 | ] 13 | 14 | objc.loadBundleFunctions(IOKit_bundle, globals(), functions) 15 | 16 | def io_key(keyname): 17 | return IORegistryEntryCreateCFProperty(IOServiceGetMatchingService(0, IOServiceMatching("IOPlatformExpertDevice")), keyname, None, 0) 18 | 19 | def get_hardware_uuid(): 20 | return io_key("IOPlatformUUID") 21 | 22 | def get_hardware_serial(): 23 | return io_key("IOPlatformSerialNumber") 24 | 25 | print get_hardware_uuid() 26 | print get_hardware_serial() 27 | -------------------------------------------------------------------------------- /set_spotlight_exclusions.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # ref: https://gist.github.com/pudquick/f150b59dd68500099a56184dc25f1300 3 | # Only tested on OSX 10.11.5 4 | 5 | import objc 6 | from Foundation import NSBundle 7 | Metadata_bundle = NSBundle.bundleWithIdentifier_('com.apple.Metadata') 8 | 9 | functions = [ 10 | ('_MDCopyExclusionList', '@'), 11 | ('_MDSetExclusion', '@@I'), 12 | ] 13 | 14 | objc.loadBundleFunctions(Metadata_bundle, globals(), functions) 15 | 16 | # get the current exclusions (returns list of path strings) 17 | current_exclusions = _MDCopyExclusionList() 18 | 19 | # add an exclusion for a path 20 | result = _MDSetExclusion('/Path/We/Want/To/Exclude', 1) 21 | 22 | # remove an exclusion for a path 23 | result = _MDSetExclusion('/Path/We/No/Longer/Want/To/Exclude', 0) 24 | -------------------------------------------------------------------------------- /softwareupdate_example.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | from CoreFoundation import ( 4 | CFPreferencesCopyAppValue, 5 | CFPreferencesAppValueIsForced 6 | ) 7 | 8 | domain = '/Library/Preferences/com.apple.SoftwareUpdate' 9 | key = 'AutomaticCheckEnabled' 10 | 11 | key_value = CFPreferencesCopyAppValue(key, domain) 12 | print 'Key Value: %s' % key_value 13 | 14 | key_forced = CFPreferencesAppValueIsForced(key, domain) 15 | print 'Key Forced: %s' % key_forced 16 | 17 | -------------------------------------------------------------------------------- /ssids.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import subprocess, plistlib, re 4 | 5 | d = {} 6 | cmd = ['/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport', '-s'] 7 | output = re.sub(' +',' ', subprocess.check_output(cmd)) 8 | l = [] 9 | for line in output.splitlines(): 10 | print len(line.strip().split(' ')) 11 | print line.strip().split(' ') 12 | offset = len(line.strip().split(' ')) - 7 13 | if offset: 14 | print offset 15 | -------------------------------------------------------------------------------- /sysctl.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | #ref https://macadmins.slack.com/files/frogor/F0JCE3YUS/-.py 3 | 4 | from ctypes import CDLL, c_uint, byref, create_string_buffer 5 | libc = CDLL('/usr/lib/libc.dylib') 6 | 7 | def sysctl(name, isString=True): 8 | size = c_uint(0) 9 | # Find out how big our buffer will be 10 | libc.sysctlbyname(name, None, byref(size), None, 0) 11 | # Make the buffer 12 | buf = create_string_buffer(size.value) 13 | # Re-run, but provide the buffer 14 | libc.sysctlbyname(name, buf, byref(size), None, 0) 15 | if isString: 16 | return buf.value 17 | else: 18 | return buf.raw 19 | 20 | print sysctl('kern.uuid') 21 | print sysctl('kern.hostname') 22 | print sysctl('hw.model') 23 | print sysctl('kern.osversion') 24 | print sysctl('kern.netboot') 25 | -------------------------------------------------------------------------------- /verify_password.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | #ref: https://gist.github.com/pudquick/3403867841b047bc0332 3 | 4 | import getpass 5 | username = getpass.getuser().decode(u"utf-8") 6 | password = getpass.getpass((u"password for %s: " % username).encode(u"utf-8")) 7 | 8 | from OpenDirectory import * 9 | 10 | session = ODSession.defaultSession() 11 | node, error = ODNode.nodeWithSession_type_error_(session, kODNodeTypeAuthentication, None) 12 | query, error = ODQuery.queryWithNode_forRecordTypes_attribute_matchType_queryValues_returnAttributes_maximumResults_error_( \ 13 | node, kODRecordTypeUsers, 14 | kODAttributeTypeRecordName, kODMatchEqualTo, username, 15 | kODAttributeTypeNativeOnly, 0, None) 16 | result, error = query.resultsAllowingPartial_error_(False, None) 17 | record = result[0] 18 | result, error = record.verifyPassword_error_(password, None) 19 | print result 20 | -------------------------------------------------------------------------------- /verify_password2.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | #ref: https://macadmins.slack.com/files/elios/F0JA6JD6V/Untitled.py 3 | 4 | from OpenDirectory import * 5 | import getpass 6 | 7 | username = getpass.getuser().decode(u"utf-8") 8 | password = getpass.getpass((u"password for %s: " % username).encode(u"utf-8")) 9 | 10 | class ODException(Exception): 11 | '''Base exception for OpenDirectory''' 12 | 13 | class ODSessionException(Exception): 14 | '''ODSessionException''' 15 | 16 | class ODNodeException(ODException): 17 | '''ODNode exception''' 18 | 19 | class ODQueryException(ODException): 20 | '''ODQueryException''' 21 | 22 | class ODRecordException(ODException): 23 | '''ODRecordException''' 24 | 25 | class ODPasswordException(ODException): 26 | '''ODPasswordException''' 27 | 28 | def ODverifyPassword(username, password, dsnode='/Search'): 29 | '''Uses the OpenDirectory framework to verify username and password. 30 | 31 | Input: username, password, and optional DS node name. 32 | Output: True if username and password are verified 33 | Exceptions: all sorts! 34 | ''' 35 | session = ODSession.defaultSession() 36 | if not session: 37 | raise ODSessionException('Could not get default Open Directory session') 38 | 39 | node, error = ODNode.nodeWithSession_name_error_(session, dsnode, None) 40 | if error: 41 | raise ODNodeException(error) 42 | 43 | query, error = ODQuery.queryWithNode_forRecordTypes_attribute_matchType_queryValues_returnAttributes_maximumResults_error_( 44 | node, 45 | kODRecordTypeUsers, 46 | kODAttributeTypeRecordName, 47 | kODMatchEqualTo, 48 | username, 49 | kODAttributeTypeStandardOnly, 50 | 1, 51 | None ) 52 | 53 | if error: 54 | raise ODQueryException(error) 55 | 56 | results, error = query.resultsAllowingPartial_error_(False, None) 57 | if error: 58 | raise ODQueryException(error) 59 | 60 | if results: 61 | record = results[0] 62 | passwordVerified, error = record.verifyPassword_error_(password, None) 63 | if error and error.code() != 5000: # 5000 means invalid user or password 64 | raise ODPasswordException(error) 65 | 66 | return passwordVerified 67 | else: 68 | # no matching username in DS, so return False 69 | return False 70 | 71 | print ODverifyPassword(username, password) 72 | -------------------------------------------------------------------------------- /visible_apps.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | #ref: https://gist.github.com/pudquick/eebc4d569100c8e3039bf3eae56bee4c 3 | 4 | from Foundation import NSBundle 5 | import objc 6 | CoreServices = NSBundle.bundleWithIdentifier_('com.apple.CoreServices') 7 | 8 | functions = [ 9 | ('_LSCopyRunningApplicationArray', '@I'), 10 | ('_LSCopyApplicationInformation', '@I@@'), 11 | ] 12 | 13 | constants = [ 14 | ('_kLSApplicationTypeKey', '@'), 15 | ('_kLSApplicationForegroundTypeKey', '@'), 16 | ('_kLSDisplayNameKey', '@') 17 | ] 18 | 19 | objc.loadBundleFunctions(CoreServices, globals(), functions) 20 | objc.loadBundleVariables(CoreServices, globals(), constants) 21 | 22 | apps = _LSCopyRunningApplicationArray(0xfffffffe) 23 | app_infos = [_LSCopyApplicationInformation(0xffffffff, x, None) for x in apps] 24 | visible_app_infos = [x for x in app_infos if x.get(_kLSApplicationTypeKey, None) == _kLSApplicationForegroundTypeKey] 25 | visible_apps = sorted([x.get(_kLSDisplayNameKey) for x in visible_app_infos]) 26 | 27 | #print visible_app_infos 28 | print visible_apps 29 | -------------------------------------------------------------------------------- /vm_status.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | # ref: https://gist.github.com/pudquick/8107bb7b6e8d63eaddec7042c081e656 4 | from ctypes import CDLL, c_uint, byref, create_string_buffer 5 | libc = CDLL('/usr/lib/libc.dylib') 6 | 7 | def sysctl(name): 8 | size = c_uint(0) 9 | libc.sysctlbyname(name, None, byref(size), None, 0) 10 | buf = create_string_buffer(size.value) 11 | libc.sysctlbyname(name, buf, byref(size), None, 0) 12 | return buf.value 13 | 14 | def is_mac_vm(): 15 | return 'VMM' in sysctl('machdep.cpu.features').split(' ') 16 | 17 | print is_mac_vm() 18 | -------------------------------------------------------------------------------- /waitfornetwork.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | #ref: nick.mcspadden in slack 3 | 4 | import subprocess, sys 5 | 6 | def wait_for_network(): 7 | """Wait until network access is up.""" 8 | # Wait up to 180 seconds for scutil dynamic store to register DNS 9 | cmd = [ 10 | '/usr/sbin/scutil', 11 | '-w', 'State:/Network/Global/DNS', 12 | '-t', '180' 13 | ] 14 | task = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 15 | out, err = task.communicate() 16 | if task.returncode != 0: 17 | print "Network did not come up after 3 minutes. Aborting" 18 | sys.exit(1) 19 | return True 20 | 21 | print wait_for_network() 22 | --------------------------------------------------------------------------------