From d9618fcf7c64236a4fe205ddd9fca0ddd90a6ade Mon Sep 17 00:00:00 2001 From: Arjen van Bochoven Date: Wed, 18 Jun 2014 15:49:44 +0200 Subject: [PATCH 01/21] Bump version number --- app/helpers/site_helper.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/helpers/site_helper.php b/app/helpers/site_helper.php index 101888dd7..2ee34ac78 100644 --- a/app/helpers/site_helper.php +++ b/app/helpers/site_helper.php @@ -1,7 +1,7 @@ Date: Wed, 18 Jun 2014 17:16:40 +0200 Subject: [PATCH 02/21] Fix datetime in munkireport tab --- app/helpers/site_helper.php | 2 +- app/views/client/munki_tab.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/helpers/site_helper.php b/app/helpers/site_helper.php index 2ee34ac78..5f7b7d430 100644 --- a/app/helpers/site_helper.php +++ b/app/helpers/site_helper.php @@ -1,7 +1,7 @@ $(document).ready(function() { $( "table time" ).each(function( index ) { - $(this).html(moment($(this).attr('datetime')).fromNow()); + $(this).html(moment($(this).attr('datetime'), "YYYY-MM-DD HH:mm:ss Z").fromNow()); }); }); From a60479be8f2898ac47ca5b3375948d21ec758b82 Mon Sep 17 00:00:00 2001 From: "Noel B. A." Date: Wed, 18 Jun 2014 23:36:43 +0300 Subject: [PATCH 03/21] Added four more vendors to display listing --- app/views/listing/displays.php | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/app/views/listing/displays.php b/app/views/listing/displays.php index 53328e6ee..88c7797bd 100644 --- a/app/views/listing/displays.php +++ b/app/views/listing/displays.php @@ -96,7 +96,19 @@ vendor="SMART Technologies" break; case "9d1": - vendor="BenQ" + vendor="BenQ" + break; + case "4dd9": + vendor="Sony" + break; + case "472": + vendor="Acer" + break; + case "22f0": + vendor="HP" + break; + case "34ac": + vendor="Mitsubishi" break; } $('td:eq(3)', nRow).html(vendor) From 9a05fff9abfdea8a330564bcbd3e46a574766fe1 Mon Sep 17 00:00:00 2001 From: Marnin Date: Wed, 18 Jun 2014 09:01:29 -0400 Subject: [PATCH 04/21] Add sitename to login page --- app/views/auth/login.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/auth/login.php b/app/views/auth/login.php index af08bf386..542f1d51e 100644 --- a/app/views/auth/login.php +++ b/app/views/auth/login.php @@ -11,7 +11,7 @@ - + - From 8e038e4b4c0553f26a97d2d330364d75cf9a48ed Mon Sep 17 00:00:00 2001 From: Arjen van Bochoven Date: Thu, 19 Jun 2014 16:42:22 +0200 Subject: [PATCH 05/21] Added Compaq --- app/views/listing/displays.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/views/listing/displays.php b/app/views/listing/displays.php index 53328e6ee..43c1bd622 100644 --- a/app/views/listing/displays.php +++ b/app/views/listing/displays.php @@ -98,6 +98,9 @@ case "9d1": vendor="BenQ" break; + case "22f0" + vendor="Compaq" + break; } $('td:eq(3)', nRow).html(vendor) From 9abc5df946ee0b1507e5b41ba19800ce92bb1057 Mon Sep 17 00:00:00 2001 From: Arjen van Bochoven Date: Thu, 19 Jun 2014 16:48:26 +0200 Subject: [PATCH 06/21] Fix vendor --- app/helpers/site_helper.php | 2 +- app/views/listing/displays.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/helpers/site_helper.php b/app/helpers/site_helper.php index 5f7b7d430..4c3a3b040 100644 --- a/app/helpers/site_helper.php +++ b/app/helpers/site_helper.php @@ -1,7 +1,7 @@ Date: Thu, 19 Jun 2014 16:50:02 +0200 Subject: [PATCH 07/21] Compaq is now HP --- app/helpers/site_helper.php | 2 +- app/views/listing/displays.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/helpers/site_helper.php b/app/helpers/site_helper.php index 4c3a3b040..6cfd8ee38 100644 --- a/app/helpers/site_helper.php +++ b/app/helpers/site_helper.php @@ -1,7 +1,7 @@ Date: Thu, 19 Jun 2014 22:51:39 +0200 Subject: [PATCH 08/21] Added uptime to reportdata --- app/helpers/site_helper.php | 2 +- .../002_reportdata_add_uptime.php | 77 +++++++++++++++++++ app/modules/reportdata/reportdata_model.php | 3 +- app/views/client/client_detail.php | 14 ++++ app/views/client/filevault_tab.php | 11 --- app/views/client/machine_info.php | 14 ++-- assets/client_installer/postflight | 1 + assets/client_installer/reportcommon | 36 +++++++++ assets/client_installer/submit.preflight | 1 + 9 files changed, 137 insertions(+), 22 deletions(-) create mode 100644 app/migrations/reportdata_model/002_reportdata_add_uptime.php diff --git a/app/helpers/site_helper.php b/app/helpers/site_helper.php index 6cfd8ee38..098e6229b 100644 --- a/app/helpers/site_helper.php +++ b/app/helpers/site_helper.php @@ -1,7 +1,7 @@ tablename = 'reportdata'; + } + + public function up() + { + // Get database handle + $dbh = $this->getdbh(); + + // Wrap in transaction + $dbh->beginTransaction(); + + // Adding a column is simple... + $sql = sprintf('ALTER TABLE %s ADD COLUMN %s INTEGER', + $this->enquote($this->tablename), $this->enquote($this->columname)); + + $this->exec($sql); + + $dbh->commit(); + } + + public function down() + { + // Get database handle + $dbh = $this->getdbh(); + + switch ($this->get_driver()) + { + case 'sqlite':// ...removing a column in SQLite is hard + + $dbh->beginTransaction(); + + // Create temporary table + $sql = "CREATE TABLE %s_temp ( + id INTEGER PRIMARY KEY, + serial_number VARCHAR(255) UNIQUE, + console_user VARCHAR(255), + long_username VARCHAR(255), + remote_ip VARCHAR(255), + reg_timestamp INTEGER, + timestamp INTEGER)"; + $this->exec(sprintf($sql, $this->tablename)); + + $sql = "INSERT INTO %s_temp + SELECT id, serial_number, console_user, long_username, remote_ip, reg_timestamp, timestamp + FROM %s"; + $this->exec(sprintf($sql, $this->tablename, $this->tablename)); + + $sql = "DROP table %s"; + $this->exec(sprintf($sql, $this->tablename)); + + $sql = "ALTER TABLE %s_temp RENAME TO %s"; + $this->exec(sprintf($sql, $this->tablename, $this->tablename)); + + $dbh->commit(); + + break; + + default: // MySQL (other engines?) + + // MySQL drops the index as well -> check for other engines + $sql = sprintf('ALTER TABLE %s DROP COLUMN %s', + $this->enquote($this->tablename), $this->enquote($this->columname)); + $this->exec($sql); + } + } +} diff --git a/app/modules/reportdata/reportdata_model.php b/app/modules/reportdata/reportdata_model.php index 0fa439a78..94dae10d8 100644 --- a/app/modules/reportdata/reportdata_model.php +++ b/app/modules/reportdata/reportdata_model.php @@ -10,11 +10,12 @@ function __construct($serial='') $this->rs['console_user'] = ''; $this->rs['long_username'] = ''; $this->rs['remote_ip'] = ''; + $this->rs['uptime'] = 0; // Uptime in seconds $this->rs['reg_timestamp'] = time(); // Registration date $this->rs['timestamp'] = time(); // Schema version, increment when creating a db migration - $this->schema_version = 1; + $this->schema_version = 2; // Create indexes diff --git a/app/views/client/client_detail.php b/app/views/client/client_detail.php index 6ebff4cac..179012894 100644 --- a/app/views/client/client_detail.php +++ b/app/views/client/client_detail.php @@ -105,6 +105,20 @@ document.body.scrollTop=yScroll; } }) + + // Set times + $( "dd time" ).each(function( index ) { + if($(this).hasClass('absolutetime')) + { + seconds = moment().seconds(parseInt($(this).attr('datetime'))) + $(this).html(moment(seconds).fromNow(true)); + } + else + { + $(this).html(moment($(this).attr('datetime') * 1000).fromNow()); + } + $(this).tooltip().css('cursor', 'pointer'); + }); }); diff --git a/app/views/client/filevault_tab.php b/app/views/client/filevault_tab.php index 225cbd930..d181f5f5e 100644 --- a/app/views/client/filevault_tab.php +++ b/app/views/client/filevault_tab.php @@ -5,17 +5,6 @@ ?> - - - -

FileVault Escrow

diff --git a/app/views/client/machine_info.php b/app/views/client/machine_info.php index 52a8b4f83..4e33a2a3a 100755 --- a/app/views/client/machine_info.php +++ b/app/views/client/machine_info.php @@ -106,6 +106,11 @@
purchase_date?>
+
+
Uptime
+
+
+
Registration date
@@ -113,15 +118,6 @@
- - diff --git a/assets/client_installer/postflight b/assets/client_installer/postflight index e304ac1fa..b83397919 100755 --- a/assets/client_installer/postflight +++ b/assets/client_installer/postflight @@ -26,6 +26,7 @@ def main(): report_info['console_user'] = "%s" % munkicommon.getconsoleuser() report_info['runtype'] = runtype report_info['runstate'] = 'done' + report_info['uptime'] = reportcommon.get_uptime() report_info_plist = FoundationPlist.writePlistToString(report_info) items = {'reportdata':{'hash':hashlib.md5(report_info_plist).hexdigest(), \ 'data':report_info_plist}} diff --git a/assets/client_installer/reportcommon b/assets/client_installer/reportcommon index 1e5c2b13b..9a1541ea1 100644 --- a/assets/client_installer/reportcommon +++ b/assets/client_installer/reportcommon @@ -16,6 +16,9 @@ from Foundation import CFPreferencesSetValue from Foundation import kCFPreferencesAnyUser from Foundation import kCFPreferencesCurrentUser from Foundation import kCFPreferencesCurrentHost +import ctypes +import struct +import time # our preferences "bundle_id" @@ -66,6 +69,39 @@ def get_cpuinfo(): output = output.strip() return output.decode('utf-8') +def get_uptime(): + """Returns uptime in seconds or None, on BSD (including OS X). + Copied from https://pypi.python.org/pypi/uptime/1.0.0""" + try: + libc = ctypes.CDLL('libc.so') + except OSError: + # OS X; can't use ctypes.util.find_library because that creates + # a new process on Linux, which is undesirable. + try: + libc = ctypes.CDLL('libc.dylib') + except OSError: + return None + + if not hasattr(libc, 'sysctlbyname'): + # Not BSD + return None + + # Determine how much space we need for the response + sz = ctypes.c_uint(0) + libc.sysctlbyname('kern.boottime', None, ctypes.byref(sz), None, 0) + if sz.value != struct.calcsize('@LL'): + # Unexpected, let's give up. + return None + + # For real now + buf = ctypes.create_string_buffer(sz.value) + libc.sysctlbyname('kern.boottime', buf, ctypes.byref(sz), None, 0) + sec, usec = struct.unpack('@LL', buf.raw) + + boottime = sec + usec / 1000000 + up = int(time.time() - boottime) + return up if up > 0 else None + def set_pref(pref_name, pref_value): """Sets a preference, See munkicommon.py for details""" CFPreferencesSetValue( diff --git a/assets/client_installer/submit.preflight b/assets/client_installer/submit.preflight index d0eae3f44..a163fa27e 100755 --- a/assets/client_installer/submit.preflight +++ b/assets/client_installer/submit.preflight @@ -37,6 +37,7 @@ def main(): report_info['long_username'] = reportcommon.get_long_username(report_info['console_user']) report_info['runtype'] = runtype report_info['runstate'] = 'done' + report_info['uptime'] = reportcommon.get_uptime() report_info_plist = FoundationPlist.writePlistToString(report_info) items = {'machine': \ From cc28021c1e99a5126e527a02af6373787d12d335 Mon Sep 17 00:00:00 2001 From: Arjen van Bochoven Date: Sun, 22 Jun 2014 00:29:55 +0200 Subject: [PATCH 09/21] Add default value to uptime --- app/migrations/reportdata_model/002_reportdata_add_uptime.php | 2 +- app/modules/reportdata/reportdata_model.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/migrations/reportdata_model/002_reportdata_add_uptime.php b/app/migrations/reportdata_model/002_reportdata_add_uptime.php index 81ee37ad3..ff74a0061 100644 --- a/app/migrations/reportdata_model/002_reportdata_add_uptime.php +++ b/app/migrations/reportdata_model/002_reportdata_add_uptime.php @@ -21,7 +21,7 @@ public function up() $dbh->beginTransaction(); // Adding a column is simple... - $sql = sprintf('ALTER TABLE %s ADD COLUMN %s INTEGER', + $sql = sprintf('ALTER TABLE %s ADD COLUMN %s INTEGER DEFAULT 0', $this->enquote($this->tablename), $this->enquote($this->columname)); $this->exec($sql); diff --git a/app/modules/reportdata/reportdata_model.php b/app/modules/reportdata/reportdata_model.php index 94dae10d8..3f6ebb0d6 100644 --- a/app/modules/reportdata/reportdata_model.php +++ b/app/modules/reportdata/reportdata_model.php @@ -10,7 +10,7 @@ function __construct($serial='') $this->rs['console_user'] = ''; $this->rs['long_username'] = ''; $this->rs['remote_ip'] = ''; - $this->rs['uptime'] = 0; // Uptime in seconds + $this->rs['uptime'] = 0; $this->rt['uptime'] = 'INTEGER DEFAULT 0';// Uptime in seconds $this->rs['reg_timestamp'] = time(); // Registration date $this->rs['timestamp'] = time(); From 8a5b20b3db51cc1337787d84da880a659e2d6c12 Mon Sep 17 00:00:00 2001 From: Arjen van Bochoven Date: Sun, 22 Jun 2014 00:47:07 +0200 Subject: [PATCH 10/21] If no uptime, list as unavailable --- app/helpers/site_helper.php | 2 +- app/views/client/machine_info.php | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/app/helpers/site_helper.php b/app/helpers/site_helper.php index 098e6229b..c446dadf7 100644 --- a/app/helpers/site_helper.php +++ b/app/helpers/site_helper.php @@ -1,7 +1,7 @@
Uptime
+ uptime):?>
+ +
+
From 5a0ed701c2e607f731859da6f69aa06ae7e65ee3 Mon Sep 17 00:00:00 2001 From: Arjen van Bochoven Date: Mon, 23 Jun 2014 09:30:50 +0200 Subject: [PATCH 11/21] Small fixes for uptime() --- app/views/client/machine_info.php | 2 +- assets/client_installer/reportcommon | 22 ++++++++-------------- 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/app/views/client/machine_info.php b/app/views/client/machine_info.php index 559edc92e..31bb6f72c 100755 --- a/app/views/client/machine_info.php +++ b/app/views/client/machine_info.php @@ -108,7 +108,7 @@
Uptime
- uptime):?> + uptime > 0):?>
diff --git a/assets/client_installer/reportcommon b/assets/client_installer/reportcommon index 9a1541ea1..a5c76a3ec 100644 --- a/assets/client_installer/reportcommon +++ b/assets/client_installer/reportcommon @@ -73,34 +73,28 @@ def get_uptime(): """Returns uptime in seconds or None, on BSD (including OS X). Copied from https://pypi.python.org/pypi/uptime/1.0.0""" try: - libc = ctypes.CDLL('libc.so') + libc = ctypes.CDLL('libc.dylib') except OSError: - # OS X; can't use ctypes.util.find_library because that creates - # a new process on Linux, which is undesirable. - try: - libc = ctypes.CDLL('libc.dylib') - except OSError: - return None - + return -1 + + return -1 if not hasattr(libc, 'sysctlbyname'): # Not BSD - return None + return -1 # Determine how much space we need for the response sz = ctypes.c_uint(0) libc.sysctlbyname('kern.boottime', None, ctypes.byref(sz), None, 0) if sz.value != struct.calcsize('@LL'): # Unexpected, let's give up. - return None + return -1 # For real now buf = ctypes.create_string_buffer(sz.value) libc.sysctlbyname('kern.boottime', buf, ctypes.byref(sz), None, 0) sec, usec = struct.unpack('@LL', buf.raw) - - boottime = sec + usec / 1000000 - up = int(time.time() - boottime) - return up if up > 0 else None + up = int(time.time() - sec) + return up if up > 0 else -1 def set_pref(pref_name, pref_value): """Sets a preference, See munkicommon.py for details""" From 23d027366160a680e34c1a91c8658dbd727b698c Mon Sep 17 00:00:00 2001 From: Arjen van Bochoven Date: Mon, 23 Jun 2014 09:36:05 +0200 Subject: [PATCH 12/21] Removed debugging code --- assets/client_installer/reportcommon | 1 - 1 file changed, 1 deletion(-) diff --git a/assets/client_installer/reportcommon b/assets/client_installer/reportcommon index a5c76a3ec..779bd3db5 100644 --- a/assets/client_installer/reportcommon +++ b/assets/client_installer/reportcommon @@ -77,7 +77,6 @@ def get_uptime(): except OSError: return -1 - return -1 if not hasattr(libc, 'sysctlbyname'): # Not BSD return -1 From 821fb6a207beff32a00a59ea1a013755e88c6072 Mon Sep 17 00:00:00 2001 From: "Noel B. A" Date: Mon, 23 Jun 2014 18:58:06 +0300 Subject: [PATCH 13/21] Improved displays.py stability. Fixes #117 --- app/modules/displays_info/scripts/displays.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/app/modules/displays_info/scripts/displays.py b/app/modules/displays_info/scripts/displays.py index e5d80bba5..b4bb11419 100755 --- a/app/modules/displays_info/scripts/displays.py +++ b/app/modules/displays_info/scripts/displays.py @@ -35,15 +35,19 @@ #loop within each display for display in vga['spdisplays_ndrvs']: - #Serial and Type sections + #Type section + try: + if display['_spdisplays_display-vendor-id'] == "610": + result += 'Type = Internal' + else: + result += 'Type = External' + except KeyError as error: #this catches errors in headless Xserve and minis + result += 'Type = Internal' + + #Serial section if display.get('spdisplays_display-serial-number', None): - result += 'Type = External' result += '\nSerial = ' + str(display['spdisplays_display-serial-number']) - elif display['_spdisplays_display-vendor-id'] != "610": - result += 'Type = External' - result += '\nSerial = n/a' else: - result += 'Type = Internal' result += '\nSerial = n/a' try: From 84ab031f4d762b4508d440ec31ebad7ee00e201c Mon Sep 17 00:00:00 2001 From: diwanicki Date: Tue, 24 Jun 2014 17:04:09 -0400 Subject: [PATCH 14/21] Update displays.php Added three additional monitor manufacturers --- app/views/listing/displays.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/app/views/listing/displays.php b/app/views/listing/displays.php index be07596cb..d9be936d1 100644 --- a/app/views/listing/displays.php +++ b/app/views/listing/displays.php @@ -113,6 +113,15 @@ case "22f0": vendor="HP" break; + case "5a63": + vendor="ViewSonic" + break; + case "4c2d": + vendor="Samsung" + break; + case "593a": + vendor="Vizio" + break; } $('td:eq(3)', nRow).html(vendor) From 6b3385054b597f584086e88597870726e1647e62 Mon Sep 17 00:00:00 2001 From: Arjen van Bochoven Date: Tue, 24 Jun 2014 23:39:21 +0200 Subject: [PATCH 15/21] Exit(0) in preflight Prevent preflight from emitting a nonzero exit status which hangs up munki --- app/helpers/site_helper.php | 2 +- assets/client_installer/preflight | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/helpers/site_helper.php b/app/helpers/site_helper.php index c446dadf7..199e188f0 100644 --- a/app/helpers/site_helper.php +++ b/app/helpers/site_helper.php @@ -1,7 +1,7 @@ Date: Wed, 25 Jun 2014 14:14:12 +0200 Subject: [PATCH 16/21] Use munki verbosity settings for messages --- app/helpers/site_helper.php | 2 +- assets/client_installer/postflight | 21 ++++++++----- assets/client_installer/preflight | 22 ++++++++----- assets/client_installer/reportcommon | 47 +++++++++++++++++++--------- 4 files changed, 63 insertions(+), 29 deletions(-) diff --git a/app/helpers/site_helper.php b/app/helpers/site_helper.php index 199e188f0..51faea210 100644 --- a/app/helpers/site_helper.php +++ b/app/helpers/site_helper.php @@ -1,7 +1,7 @@ > sys.stderr, str(e) + reportcommon.display_error( str(e) ) # Skip this script else: # /usr/local/munki/postflight.d does not exist diff --git a/assets/client_installer/preflight b/assets/client_installer/preflight index a871cb518..e40646419 100755 --- a/assets/client_installer/preflight +++ b/assets/client_installer/preflight @@ -3,12 +3,20 @@ ''' Preflight script''' import sys import os +from munkilib import reportcommon def main(): '''Main''' # Munkireport submit script TODO: make config item submitscript = 'submit.preflight' + # Try to get the munki verbosity level + verbosity = os.environ.get('MUNKI_VERBOSITY_LEVEL') + if (verbosity): + reportcommon.set_verbosity(int(verbosity)) + else: + reportcommon.set_verbosity(3) + # get runtype if (len(sys.argv) > 1): runtype = sys.argv[1] @@ -27,7 +35,7 @@ def main(): try: sub = files.pop(files.index(submitscript)) except Exception, e: - print '%s not found in preflight.d' % submitscript + reportcommon.display_error('%s not found in preflight.d' % submitscript) exit(0) # Sort list @@ -45,23 +53,23 @@ def main(): continue try: # Attempt to execute script - print 'Running %s' % preflightscript + reportcommon.display_detail('Running %s' % preflightscript) result, stdout, stderr = utils.runExternalScript( preflightscriptpath, allow_insecure=False, script_args=[runtype]) if stdout: - print(stdout.encode('UTF-8')) + reportcommon.display_detail(stdout) if stderr: - print('%s Error: %s' + reportcommon.display_detail('%s Error: %s' % (preflightscript, stderr)) if result: - print('preflight.d/%s return code: %d' + reportcommon.display_error('preflight.d/%s return code: %d' % (preflightscript, result)) - exit(0) + continue except utils.ScriptNotFoundError: pass # Script has disappeared? Well, it is not required, so pass except utils.RunExternalScriptError, e: - print >> sys.stderr, str(e) + reportcommon.display_error(str(e)) # Skip this script else: # /usr/local/munki/preflight.d does not exist diff --git a/assets/client_installer/reportcommon b/assets/client_installer/reportcommon index 779bd3db5..0d832e5fe 100644 --- a/assets/client_installer/reportcommon +++ b/assets/client_installer/reportcommon @@ -24,23 +24,42 @@ import time # our preferences "bundle_id" BUNDLE_ID = 'MunkiReport' +def set_verbosity(level): + """ + Set verbosity level + """ + munkicommon.verbose = int(level) + +def display_error(msg, *args): + """ + Call munkicommon error msg handler + """ + munkicommon.display_error('Munkireport: %s' % msg, *args) + +def display_detail(msg, *args): + """ + Call munkicommon detail msg handler + """ + munkicommon.display_detail('Munkireport: %s' % msg, *args) + def curl(url, values): req = urllib2.Request(url, urllib.urlencode(values)) try: response = urllib2.urlopen(req) except urllib2.URLError, e: + display_error(url) if hasattr(e, 'reason'): - print url - print 'We failed to reach a server.' - print 'Reason: ', e.reason + display_error('We failed to reach a server') + display_error('Reason: %s' % e.reason) elif hasattr(e, 'code'): - print url - print 'The server couldn\'t fulfill the request.' - print 'Error code: ', e.code + display_error('The server couldn\'t fulfill the request.') + display_error('Error code: %s' % e.code) + else: + display_error('Miscellaneous server error.') # Reportserver error, exit clean so munki keeps running exit(0) except: - print 'A server error occurred:', sys.exc_info()[0] + display_error('A server error occurred: %s' % sys.exc_info()[0]) exit(0) return response @@ -154,32 +173,32 @@ def process(serial,items): try: result = unserialize(server_data) except: - print 'Illegal response from the server: %s' % server_data + display_error('Illegal response from the server: %s' % server_data) return -1 if result.get('error') != '': - print 'Server error: %s' % result['error'] + display_error('Server error: %s' % result['error']) return -1 # Retrieve hashes that need updating for i in items.keys(): if i in result: - print 'Need to update ' + i + display_detail('Need to update %s' % i) if items[i].get('path'): try: f = open(items[i]['path'], "r") items[i]['data'] = f.read() except: - print "ERROR: Can't open %s" % items[i]['path'] + display_error("ERROR: Can't open %s" % items[i]['path']) else: # delete items that don't have to be uploaded del items[i] # Send new files with hashes if len(items): - print 'Sending items' + display_detail('Sending items') response = curl(checkurl, {'serial': serial, 'items': serialize(items), 'passphrase': passphrase}) - print response.read() + display_detail(response.read()) else: - print 'No changes' + display_detail('No changes') From ede12c577f9a97f40b1f675e20a3da769f065ceb Mon Sep 17 00:00:00 2001 From: "Noel B. A" Date: Thu, 26 Jun 2014 17:56:06 +0300 Subject: [PATCH 17/21] fixes an error introduced in 821fb6a where all Apple monitors where counted as built-in --- app/modules/displays_info/scripts/displays.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/modules/displays_info/scripts/displays.py b/app/modules/displays_info/scripts/displays.py index b4bb11419..c3346352a 100755 --- a/app/modules/displays_info/scripts/displays.py +++ b/app/modules/displays_info/scripts/displays.py @@ -37,11 +37,13 @@ #Type section try: - if display['_spdisplays_display-vendor-id'] == "610": + if display.get('spdisplays_display-serial-number', None): + result += 'Type = External' + elif display['_spdisplays_display-vendor-id'] == "610": result += 'Type = Internal' else: result += 'Type = External' - except KeyError as error: #this catches errors in headless Xserve and minis + except KeyError as error: #this catches the error for 10.6 where there is no vendor for built-in displays result += 'Type = Internal' #Serial section From d23a1354a4e872a5768d8c2c13d6205c1cfe9dbb Mon Sep 17 00:00:00 2001 From: Arjen van Bochoven Date: Thu, 26 Jun 2014 23:01:05 +0200 Subject: [PATCH 18/21] Added timeout for external scripts --- assets/client_installer/postflight | 2 +- assets/client_installer/preflight | 2 +- assets/client_installer/reportcommon | 54 +++++++++++++++++++++++++++- 3 files changed, 55 insertions(+), 3 deletions(-) diff --git a/assets/client_installer/postflight b/assets/client_installer/postflight index 51e53bf70..77ea01842 100755 --- a/assets/client_installer/postflight +++ b/assets/client_installer/postflight @@ -75,7 +75,7 @@ def run_postflightd_scripts(runtype): try: # Attempt to execute script reportcommon.display_detail('Running %s' % postflightscript) - result, stdout, stderr = utils.runExternalScript( + result, stdout, stderr = reportcommon.runExternalScriptWithTimeout( postflightscriptpath, allow_insecure=False, script_args=[runtype]) if stdout: reportcommon.display_detail(stdout) diff --git a/assets/client_installer/preflight b/assets/client_installer/preflight index e40646419..8f137bdc5 100755 --- a/assets/client_installer/preflight +++ b/assets/client_installer/preflight @@ -54,7 +54,7 @@ def main(): try: # Attempt to execute script reportcommon.display_detail('Running %s' % preflightscript) - result, stdout, stderr = utils.runExternalScript( + result, stdout, stderr = reportcommon.runExternalScriptWithTimeout( preflightscriptpath, allow_insecure=False, script_args=[runtype]) if stdout: reportcommon.display_detail(stdout) diff --git a/assets/client_installer/reportcommon b/assets/client_installer/reportcommon index 0d832e5fe..ca951dc77 100644 --- a/assets/client_installer/reportcommon +++ b/assets/client_installer/reportcommon @@ -20,7 +20,6 @@ import ctypes import struct import time - # our preferences "bundle_id" BUNDLE_ID = 'MunkiReport' @@ -202,3 +201,56 @@ def process(serial,items): else: display_detail('No changes') +def runExternalScriptWithTimeout(script, allow_insecure=False, script_args=(), timeout=10): + """Run a script (e.g. preflight/postflight) and return its exit status. + + Args: + script: string path to the script to execute. + allow_insecure: bool skip the permissions check of executable. + args: args to pass to the script. + Returns: + Tuple. (integer exit status from script, str stdout, str stderr). + Raises: + ScriptNotFoundError: the script was not found at the given path. + RunExternalScriptError: there was an error running the script. + """ + from munkilib import utils + import os + + if not os.path.exists(script): + raise ScriptNotFoundError('script does not exist: %s' % script) + + if not allow_insecure: + try: + utils.verifyFileOnlyWritableByMunkiAndRoot(script) + except utils.VerifyFilePermissionsError, e: + msg = ('Skipping execution due to failed file permissions ' + 'verification: %s\n%s' % (script, str(e))) + raise utils.RunExternalScriptError(msg) + + if os.access(script, os.X_OK): + cmd = [script] + if script_args: + cmd.extend(script_args) + proc = subprocess.Popen(cmd, shell=False, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + while timeout > 0: + if proc.poll() is not None: + (stdout, stderr) = proc.communicate() + return proc.returncode, stdout.decode('UTF-8','replace'), \ + stderr.decode('UTF-8','replace') + time.sleep(0.1) + timeout -= 0.1 + else: + try: + proc.kill() + except OSError as e: + if e.errno != 3: + raise + raise utils.RunExternalScriptError('%s timed out' % script) + return (0, None, None) + + else: + raise utils.RunExternalScriptError('%s not executable' % script) From 15dbbfece592fc0bf5cc121eed0bf472463338b6 Mon Sep 17 00:00:00 2001 From: Matt Cahill Date: Fri, 27 Jun 2014 14:25:15 +1200 Subject: [PATCH 19/21] fix for bluetooth preflight on 10.6 --- app/modules/bluetooth/scripts/bluetooth.sh | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/app/modules/bluetooth/scripts/bluetooth.sh b/app/modules/bluetooth/scripts/bluetooth.sh index 451a5f766..38c55ed5f 100755 --- a/app/modules/bluetooth/scripts/bluetooth.sh +++ b/app/modules/bluetooth/scripts/bluetooth.sh @@ -17,13 +17,9 @@ bluetoothfile="$DIR/cache/bluetoothinfo.txt" # echo "Getting Bluetooth device status" -# Bluetooth status. Returns '0' for off and '1' for on -Power=`defaults read /Library/Preferences/com.apple.Bluetooth.plist ControllerPowerState` -if [ $Power = 0 ]; then - status="Status = Bluetooth is off" - else - status="Status = Bluetooth is on" -fi +# Bluetooth status. +Power=`system_profiler SPBluetoothDataType | grep 'Bluetooth Power' | awk '{print tolower($3)}'` +status="Status = Bluetooth is $Power" KeyboardPercent=`ioreg -c AppleBluetoothHIDKeyboard | grep BatteryPercent | sed 's/[a-z,A-Z, ,|,",=]//g' | tail -1 | awk '{print $1}'` if [ "${KeyboardPercent}" = "" ]; then From 7f91cf2fb3cd008c3434a089f2a9b3641a05c0fa Mon Sep 17 00:00:00 2001 From: Arjen van Bochoven Date: Sat, 28 Jun 2014 00:19:52 +0200 Subject: [PATCH 20/21] Added preflight_abort.d Scripts in preflight_abort.d will abort munki when exit status <> 0 --- app/views/install/install_script.php | 3 ++ assets/client_installer/postflight | 49 ++---------------- assets/client_installer/preflight | 54 +++----------------- assets/client_installer/reportcommon | 76 +++++++++++++++++++++++++++- 4 files changed, 88 insertions(+), 94 deletions(-) diff --git a/app/views/install/install_script.php b/app/views/install/install_script.php index a6de4a047..ceea5d8bf 100644 --- a/app/views/install/install_script.php +++ b/app/views/install/install_script.php @@ -48,6 +48,9 @@ # Create postflight.d mkdir -p "${MUNKIPATH}postflight.d" +# Create preflight_abort.d +mkdir -p "${MUNKIPATH}preflight_abort.d" + echo "Configuring munkireport" #### Configure Munkireport #### diff --git a/assets/client_installer/postflight b/assets/client_installer/postflight index 77ea01842..45b0717f2 100755 --- a/assets/client_installer/postflight +++ b/assets/client_installer/postflight @@ -47,54 +47,13 @@ def main(): reportcommon.process(serial, items) - run_postflightd_scripts(runtype) - - exit(0) - -def run_postflightd_scripts(runtype): - # define path to directory with postflight scripts scriptdir = os.path.realpath(os.path.dirname(sys.argv[0])) - postflightscriptdir = os.path.join(scriptdir, "postflight.d") - if os.path.exists(postflightscriptdir): - from munkilib import utils - # Get all files in postflight.d - files = os.listdir(postflightscriptdir) - - # Sort list - files.sort() - for postflightscript in files: - if postflightscript.startswith('.'): - # Skip files that start with a period - continue - postflightscriptpath = os.path.join( - postflightscriptdir, postflightscript) - if os.path.isdir(postflightscriptpath): - # Skip directories in postflight.d directory - continue - try: - # Attempt to execute script - reportcommon.display_detail('Running %s' % postflightscript) - result, stdout, stderr = reportcommon.runExternalScriptWithTimeout( - postflightscriptpath, allow_insecure=False, script_args=[runtype]) - if stdout: - reportcommon.display_detail(stdout) - if stderr: - reportcommon.display_detail('%s Error: %s' - % (postflightscript, stderr)) - if result: - reportcommon.display_error('postflight.d/%s return code: %d' - % (postflightscript, result)) - continue + # Try to run postflight.d + postflightscriptdir = os.path.join(scriptdir, "postflight.d") + reportcommon.rundir(postflightscriptdir, runtype, False) - except utils.ScriptNotFoundError: - pass # Script has disappeared? Well, it is not required, so pass - except utils.RunExternalScriptError, e: - reportcommon.display_error( str(e) ) - # Skip this script - else: - # /usr/local/munki/postflight.d does not exist - pass + exit(0) if __name__ == '__main__': main() diff --git a/assets/client_installer/preflight b/assets/client_installer/preflight index 8f137bdc5..5b74bfca2 100755 --- a/assets/client_installer/preflight +++ b/assets/client_installer/preflight @@ -25,56 +25,14 @@ def main(): # define path to directory with preflight scripts scriptdir = os.path.realpath(os.path.dirname(sys.argv[0])) - preflightscriptdir = os.path.join(scriptdir, "preflight.d") - - if os.path.exists(preflightscriptdir): - from munkilib import utils - # Get all files in preflight.d - files = os.listdir(preflightscriptdir) - # Get munkireport submit script - try: - sub = files.pop(files.index(submitscript)) - except Exception, e: - reportcommon.display_error('%s not found in preflight.d' % submitscript) - exit(0) - - # Sort list - files.sort() - # Append submitscript - files.append(sub) - for preflightscript in files: - if preflightscript.startswith('.'): - # Skip files that start with a period - continue - preflightscriptpath = os.path.join( - preflightscriptdir, preflightscript) - if os.path.isdir(preflightscriptpath): - # Skip directories in preflight.d directory - continue - try: - # Attempt to execute script - reportcommon.display_detail('Running %s' % preflightscript) - result, stdout, stderr = reportcommon.runExternalScriptWithTimeout( - preflightscriptpath, allow_insecure=False, script_args=[runtype]) - if stdout: - reportcommon.display_detail(stdout) - if stderr: - reportcommon.display_detail('%s Error: %s' - % (preflightscript, stderr)) - if result: - reportcommon.display_error('preflight.d/%s return code: %d' - % (preflightscript, result)) - continue - except utils.ScriptNotFoundError: - pass # Script has disappeared? Well, it is not required, so pass - except utils.RunExternalScriptError, e: - reportcommon.display_error(str(e)) - # Skip this script - else: - # /usr/local/munki/preflight.d does not exist - pass + # Try to run preflight_abort.d + preflightscriptdir = os.path.join(scriptdir, "preflight_abort.d") + reportcommon.rundir(preflightscriptdir, runtype, True) + # Try to run preflight.d + preflightscriptdir = os.path.join(scriptdir, "preflight.d") + reportcommon.rundir(preflightscriptdir, runtype, False, submitscript) exit(0) diff --git a/assets/client_installer/reportcommon b/assets/client_installer/reportcommon index ca951dc77..fbe10a6c0 100644 --- a/assets/client_installer/reportcommon +++ b/assets/client_installer/reportcommon @@ -19,6 +19,7 @@ from Foundation import kCFPreferencesCurrentHost import ctypes import struct import time +import os # our preferences "bundle_id" BUNDLE_ID = 'MunkiReport' @@ -35,6 +36,12 @@ def display_error(msg, *args): """ munkicommon.display_error('Munkireport: %s' % msg, *args) +def display_warning(msg, *args): + """ + Call munkicommon warning msg handler + """ + munkicommon.display_warning('Munkireport: %s' % msg, *args) + def display_detail(msg, *args): """ Call munkicommon detail msg handler @@ -215,7 +222,6 @@ def runExternalScriptWithTimeout(script, allow_insecure=False, script_args=(), t RunExternalScriptError: there was an error running the script. """ from munkilib import utils - import os if not os.path.exists(script): raise ScriptNotFoundError('script does not exist: %s' % script) @@ -254,3 +260,71 @@ def runExternalScriptWithTimeout(script, allow_insecure=False, script_args=(), t else: raise utils.RunExternalScriptError('%s not executable' % script) + +def rundir(scriptdir, runtype, abort=False, submitscript=''): + """ + Run scripts in directory scriptdir + runtype is passed to the script + if abort is True, a non-zero exit status will abort munki + submitscript is put at the end of the scriptlist + """ + if os.path.exists(scriptdir): + + from munkilib import utils + + # Directory containing the scripts + parentdir = os.path.basename(scriptdir) + display_detail('# Executing scripts in %s' % parentdir) + + # Get all files in scriptdir + files = os.listdir(scriptdir) + + # Sort files + files.sort() + + # Find submit script and stick it on the end of the list + if submitscript: + try: + sub = files.pop(files.index(submitscript)) + files.append(sub) + except Exception, e: + display_error('%s not found in %s' + % (submitscript, parentdir)) + + for script in files: + + # Skip files that start with a period + if script.startswith('.'): + continue + + # Concatenate dir and filename + scriptpath = os.path.join(scriptdir, script) + + # Skip directories + if os.path.isdir(scriptpath): + continue + + try: + # Attempt to execute script + display_detail('Running %s' % script) + result, stdout, stderr = runExternalScriptWithTimeout( + scriptpath, allow_insecure=False, script_args=[runtype]) + if stdout: + display_detail(stdout) + if stderr: + display_detail('%s Error: %s' + % (script, stderr)) + if result: + if(abort): + display_detail('Aborted by %s' % script) + exit(1) + else: + display_warning('%s return code: %d' + % (script, result)) + + except utils.ScriptNotFoundError: + pass # Script has disappeared? Well, it is not required, so pass + except utils.RunExternalScriptError, e: + display_warning(str(e)) + +# End of reportcommon From bb7aa882517c63668e9ea3470051e23abae2fbe8 Mon Sep 17 00:00:00 2001 From: Arjen van Bochoven Date: Sun, 29 Jun 2014 09:56:53 +0200 Subject: [PATCH 21/21] Added some docs about the mr scripts --- app/helpers/site_helper.php | 2 +- docs/setup.md | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/app/helpers/site_helper.php b/app/helpers/site_helper.php index 51faea210..182312c8f 100644 --- a/app/helpers/site_helper.php +++ b/app/helpers/site_helper.php @@ -1,7 +1,7 @@