--- a/src/client.py Thu Apr 09 23:22:23 2009 -0700
+++ b/src/client.py Fri Apr 10 08:38:03 2009 -0700
@@ -74,7 +74,8 @@
from pkg.client import global_settings
from pkg.client.debugvalues import DebugValues
from pkg.client.history import (RESULT_CANCELED, RESULT_FAILED_BAD_REQUEST,
- RESULT_FAILED_CONFIGURATION, RESULT_FAILED_TRANSPORT, RESULT_FAILED_UNKNOWN)
+ RESULT_FAILED_CONFIGURATION, RESULT_FAILED_TRANSPORT, RESULT_FAILED_UNKNOWN,
+ RESULT_FAILED_OUTOFMEMORY)
from pkg.client.filelist import FileListRetrievalError
from pkg.client.retrieve import (CatalogRetrievalError,
DatastreamRetrievalError, ManifestRetrievalError)
@@ -2513,7 +2514,20 @@
#
if __name__ == "__main__":
try:
- __ret = main_func()
+ # Out of memory errors can be raised as EnvironmentErrors with
+ # an errno of ENOMEM, so in order to handle those exceptions
+ # with other errnos, we nest this try block and have the outer
+ # one handle the other instances.
+ try:
+ __ret = main_func()
+ except (MemoryError, EnvironmentError), __e:
+ if isinstance(__e, EnvironmentError) and \
+ __e.errno != errno.ENOMEM:
+ raise
+ if __img:
+ __img.history.abort(RESULT_FAILED_OUTOFMEMORY)
+ error("\n" + misc.out_of_memory())
+ __ret = 1
except SystemExit, __e:
if __img:
__img.history.abort(RESULT_FAILED_UNKNOWN)
--- a/src/modules/client/history.py Thu Apr 09 23:22:23 2009 -0700
+++ b/src/modules/client/history.py Fri Apr 10 08:38:03 2009 -0700
@@ -65,6 +65,8 @@
RESULT_FAILED_TRANSPORT = ["Failed", "Transport"]
# Indicates that the operation failed due to an actuator problem
RESULT_FAILED_ACTUATOR = ["Failed", "Actuator"]
+# Indicates that the operation failed due to not enough memory
+RESULT_FAILED_OUTOFMEMORY = ["Failed", "Out of Memory"]
# Indicates that the operation failed for an unknown reason.
RESULT_FAILED_UNKNOWN = ["Failed", "Unknown"]
@@ -88,6 +90,7 @@
api_errors.IpkgOutOfDateException: RESULT_FAILED_CONSTRAINED,
fmri.IllegalFmri: RESULT_FAILED_BAD_REQUEST,
KeyboardInterrupt: RESULT_CANCELED,
+ MemoryError: RESULT_FAILED_OUTOFMEMORY,
}
class _HistoryException(Exception):
--- a/src/modules/misc.py Thu Apr 09 23:22:23 2009 -0700
+++ b/src/modules/misc.py Fri Apr 10 08:38:03 2009 -0700
@@ -40,6 +40,7 @@
import sha
import shutil
import socket
+import struct
import sys
import time
import urllib
@@ -431,9 +432,11 @@
return False, text
-def bytes_to_str(bytes):
+def bytes_to_str(bytes, format=None):
"""Returns a human-formatted string representing the number of bytes
- in the largest unit possible."""
+ in the largest unit possible. If provided, 'format' should be a string
+ which can be formatted with a dictionary containing a float 'num' and
+ string 'unit'."""
units = [
(_("B"), 2**10),
@@ -452,8 +455,12 @@
# unit of measure's range.
continue
else:
- return "%.2f %s" % (round(bytes / float(
- limit / 2**10), 2), uom)
+ if not format:
+ format = "%(num).2f %(unit)s"
+ return format % {
+ "num": round(bytes / float(limit / 2**10), 2),
+ "unit": uom
+ }
def get_rel_path(request, uri):
# Calculate the depth of the current request path relative to our base
@@ -558,6 +565,46 @@
return fhash.hexdigest(), content.read()
+def __getvmusage():
+ """Return the amount of virtual memory in bytes currently in use."""
+
+ # This works only on Solaris, in 32-bit mode. It may not work on older
+ # or newer versions than 5.11. Ideally, we would use libproc, or check
+ # sbrk(0), but this is expedient. In most cases (there's a small chance
+ # the file will decode, but incorrectly), failure will raise an
+ # exception, and we'll fail safe.
+ try:
+ # Read just the psinfo_t, not the tacked-on lwpsinfo_t
+ psinfo_arr = file("/proc/self/psinfo").read(232)
+ psinfo = struct.unpack("6i5I4LHH6L16s80siiIIc3x7i", psinfo_arr)
+ vsz = psinfo[11] * 1024
+ except Exception:
+ vsz = None
+
+ return vsz
+
+def out_of_memory():
+ """Return an out of memory message, for use in a MemoryError handler."""
+
+ vsz = bytes_to_str(__getvmusage(), format="%(num).0f%(unit)s")
+
+ if vsz is not None:
+ error = """\
+There is not enough memory to complete the requested operation. At least
+%(vsz)s of virtual memory was in use by this command before it ran out of memory.
+You must add more memory (swap or physical) or allow the system to access more
+existing memory, or quit other programs that may be consuming memory, and try
+the operation again."""
+ else:
+ error = """\
+There is not enough memory to complete the requested operation. You must
+add more memory (swap or physical) or allow the system to access more existing
+memory, or quit other programs that may be consuming memory, and try the
+operation again."""
+
+ return _(error) % locals()
+
+
class CfgCacheError(Exception):
"""Thrown when there are errors with the cfg cache."""
def __init__(self, args=None):