summaryrefslogtreecommitdiff
path: root/prober.py
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@baserock.org>2014-02-21 04:46:49 +0000
committer <>2014-08-04 21:38:17 +0000
commitf3765db04b903b3671733e07cf1541a51966dd14 (patch)
treedefcc3c47d9b8bd78b97dcc04ee779a758d37b1c /prober.py
downloadposix-ipc-tarball-master.tar.gz
Imported from /home/lorry/working-area/delta_python-packages_posix-ipc-tarball/posix_ipc-0.9.8.tar.gz.HEADposix_ipc-0.9.8master
Diffstat (limited to 'prober.py')
-rw-r--r--prober.py427
1 files changed, 427 insertions, 0 deletions
diff --git a/prober.py b/prober.py
new file mode 100644
index 0000000..4bdcc72
--- /dev/null
+++ b/prober.py
@@ -0,0 +1,427 @@
+import subprocess
+import platform
+import os
+import sys
+
+# Set these to None for compile/link debugging or subprocess.PIPE to silence
+# compiler warnings and errors.
+STDOUT = subprocess.PIPE
+STDERR = subprocess.PIPE
+# STDOUT = None
+# STDERR = None
+
+# This is the max length that I want a printed line to be.
+MAX_LINE_LENGTH = 78
+
+PY_MAJOR_VERSION = sys.version_info[0]
+
+def line_wrap_paragraph(s):
+ # Format s with terminal-friendly line wraps.
+ done = False
+ beginning = 0
+ end = MAX_LINE_LENGTH - 1
+ lines = [ ]
+ while not done:
+ if end >= len(s):
+ done = True
+ lines.append(s[beginning:])
+ else:
+ last_space = s[beginning:end].rfind(' ')
+
+ lines.append(s[beginning:beginning + last_space])
+ beginning += (last_space + 1)
+ end = beginning + MAX_LINE_LENGTH - 1
+
+ return lines
+
+
+def print_bad_news(value_name, default):
+ s = "Setup can't determine %s on your system, so it will default to %s which may not be correct." \
+ % (value_name, default)
+ plea = "Please report this message and your operating system info to the package maintainer listed in the README file."
+
+ lines = line_wrap_paragraph(s) + [''] + line_wrap_paragraph(plea)
+
+ border = '*' * MAX_LINE_LENGTH
+
+ s = border + "\n* " + ('\n* '.join(lines)) + '\n' + border
+
+ print (s)
+
+
+def does_build_succeed(filename, linker_options = ""):
+ # Utility function that returns True if the file compiles and links
+ # successfully, False otherwise.
+ # Two things to note here --
+ # - If there's a linker option like -lrt, it needs to come *after*
+ # the specification of the C file or linking will fail on Ubuntu 11.10
+ # (maybe because of the gcc version?)
+ # - Some versions of Linux place the sem_xxx() functions in libpthread.
+ # Rather than testing whether or not it's needed, I just specify it
+ # everywhere since it's harmless to specify it when it's not needed.
+ cmd = "cc -Wall -o ./prober/foo ./prober/%s %s -lpthread" % (filename, linker_options)
+
+ p = subprocess.Popen(cmd, shell=True, stdout=STDOUT, stderr=STDERR)
+
+ # p.wait() returns the process' return code, so 0 implies that
+ # the compile & link succeeded.
+ return not bool(p.wait())
+
+
+def compile_and_run(filename, linker_options = ""):
+ # Utility function that returns the stdout output from running the
+ # compiled source file; None if the compile fails.
+ cmd = "cc -Wall -o ./prober/foo %s ./prober/%s" % (linker_options, filename)
+
+ p = subprocess.Popen(cmd, shell=True, stdout=STDOUT, stderr=STDERR)
+
+ if p.wait():
+ # uh-oh, compile failed
+ return None
+ else:
+ s = subprocess.Popen(["./prober/foo"],
+ stdout=subprocess.PIPE).communicate()[0]
+ return s.strip().decode()
+
+
+def get_sysctl_value(name):
+ """Given a sysctl name (e.g. 'kern.mqueue.maxmsg'), returns sysctl's value
+ for that variable or None if the sysctl call fails (unknown name, not
+ a BSD-ish system, etc.)
+
+ Only makes sense on systems that implement sysctl (BSD derivatives).
+ """
+ s = None
+ try:
+ # I redirect stderr to /dev/null because if sysctl is availble but
+ # doesn't know about the particular item I'm querying, it will
+ # kvetch with a message like 'second level name mqueue in
+ # kern.mqueue.maxmsg is invalid'. This always happens under OS X
+ # (which doesn't have any kern.mqueue values) and under FreeBSD when
+ # the mqueuefs kernel module isn't loaded.
+ s = subprocess.Popen(["sysctl", "-n", name],
+ stdout=subprocess.PIPE,
+ stderr=open(os.devnull, 'rw')).communicate()[0]
+ s = s.strip().decode()
+ except:
+ pass
+
+ return s
+
+
+def sniff_realtime_lib():
+ rc = None
+ filename = "sniff_realtime_lib.c"
+
+ if does_build_succeed(filename):
+ # Realtime libs not needed
+ rc = False
+ else:
+ # cc failed when not linked to realtime libs; let's try again
+ # with the realtime libs involved and see if things go better.
+ if does_build_succeed(filename, "-lrt"):
+ # Realtime libs are needed
+ rc = True
+
+ if rc == None:
+ # Unable to determine whether or not I needed the realtime libs.
+ # That's bad! Print a warning, set the return code to False
+ # and hope for the best.
+ rc = False
+ print_bad_news("if it needs to link to the realtime libraries", "'no'")
+
+ return rc
+
+
+def sniff_sem_getvalue(linker_options):
+ return does_build_succeed("sniff_sem_getvalue.c", linker_options)
+
+
+def sniff_sem_timedwait(linker_options):
+ return does_build_succeed("sniff_sem_timedwait.c", linker_options)
+
+
+def sniff_sem_value_max():
+ # default is to return None which means that it is #defined in a standard
+ # header file and doesn't need to be added to my custom header file.
+ sem_value_max = None
+
+ if not does_build_succeed("sniff_sem_value_max.c"):
+ # OpenSolaris 2008.05 doesn't #define SEM_VALUE_MAX. (This may
+ # be true elsewhere too.) Ask sysconf() instead if it exists.
+ # Note that sys.sysconf_names doesn't exist under Cygwin.
+ if hasattr(os, "sysconf_names") and \
+ ("SC_SEM_VALUE_MAX" in os.sysconf_names):
+ sem_value_max = os.sysconf("SC_SEM_VALUE_MAX")
+ else:
+ # This value of last resort should be #defined everywhere. What
+ # could possibly go wrong?
+ sem_value_max = "_POSIX_SEM_VALUE_MAX"
+
+ return sem_value_max
+
+
+def sniff_page_size():
+ DEFAULT_PAGE_SIZE = 4096
+
+ # Linker options don't matter here because I'm not calling any
+ # functions, just getting the value of a #define.
+ page_size = compile_and_run("sniff_page_size.c")
+
+ if page_size is None:
+ page_size = DEFAULT_PAGE_SIZE
+ print_bad_news("the value of PAGE_SIZE", page_size)
+
+ return page_size
+
+
+def sniff_mq_existence(linker_options):
+ return does_build_succeed("sniff_mq_existence.c", linker_options)
+
+
+def sniff_mq_prio_max():
+ # MQ_PRIO_MAX is #defined in limits.h on all of the systems that I
+ # checked that support message queues at all. (I checked 2 Linux boxes,
+ # OpenSolaris and FreeBSD 8.0.)
+
+ # 32 = minimum allowable max priority per POSIX; systems are permitted
+ # to define a larger value.
+ # ref: http://www.opengroup.org/onlinepubs/009695399/basedefs/limits.h.html
+ DEFAULT_PRIORITY_MAX = 32
+
+ max_priority = None
+ # OS X up to and including 10.8 doesn't support POSIX messages queues and
+ # doesn't define MQ_PRIO_MAX. Maybe this aggravation will cease in 10.9?
+ if does_build_succeed("sniff_mq_prio_max.c"):
+ max_priority = compile_and_run("sniff_mq_prio_max.c")
+
+ if max_priority:
+ try:
+ max_priority = int(max_priority)
+ except ValueError:
+ max_priority = None
+
+ if max_priority is None:
+ # Looking for a #define didn't work; ask sysconf() instead.
+ # Note that sys.sysconf_names doesn't exist under Cygwin.
+ if hasattr(os, "sysconf_names") and \
+ ("SC_MQ_PRIO_MAX" in os.sysconf_names):
+ max_priority = os.sysconf("SC_MQ_PRIO_MAX")
+ else:
+ max_priority = DEFAULT_PRIORITY_MAX
+ print_bad_news("the value of PRIORITY_MAX", max_priority)
+
+ # Under OS X, os.sysconf("SC_MQ_PRIO_MAX") returns -1.
+ if max_priority < 0:
+ max_priority = DEFAULT_PRIORITY_MAX
+
+ # Adjust for the fact that these are 0-based values; i.e. permitted
+ # priorities range from 0 - (MQ_PRIO_MAX - 1). So why not just make
+ # the #define one smaller? Because this one goes up to eleven...
+ max_priority -= 1
+
+ # priority is an unsigned int
+ return str(max_priority).strip() + "U"
+
+
+def sniff_mq_max_messages():
+ # This value is not defined by POSIX.
+
+ # On most systems I've tested, msg Qs are implemented via mmap-ed files
+ # or a similar interface, so the only theoretical limits are imposed by the
+ # file system. In practice, Linux and *BSD impose some fairly tight
+ # limits.
+
+ # On Linux it's available in a /proc file and often defaults to the wimpy
+ # value of 10.
+
+ # On FreeBSD (and other BSDs, I assume), it's available via sysctl as
+ # kern.mqueue.maxmsg. On my FreeBSD 9.1 test system, it defaults to 100.
+
+ # mqueue.h defines mq_attr.mq_maxmsg as a C long, so that's
+ # a practical limit for this value.
+
+ # ref: http://linux.die.net/man/7/mq_overview
+ # ref: http://www.freebsd.org/cgi/man.cgi?query=mqueuefs&sektion=5&manpath=FreeBSD+7.0-RELEASE
+ # http://fxr.watson.org/fxr/source/kern/uipc_mqueue.c?v=FREEBSD91#L195
+ # ref: http://groups.google.com/group/comp.unix.solaris/browse_thread/thread/aa223fc7c91f8c38
+ # ref: http://cygwin.com/cgi-bin/cvsweb.cgi/src/winsup/cygwin/posix_ipc.cc?cvsroot=src
+ # ref: http://cygwin.com/cgi-bin/cvsweb.cgi/src/winsup/cygwin/include/mqueue.h?cvsroot=src
+ mq_max_messages = None
+
+ # Try to get the value from where Linux stores it.
+ try:
+ mq_max_messages = int(open("/proc/sys/fs/mqueue/msg_max").read())
+ except:
+ # Oh well.
+ pass
+
+ if not mq_max_messages:
+ # Maybe we're on BSD.
+ mq_max_messages = get_sysctl_value('kern.mqueue.maxmsg')
+ if mq_max_messages:
+ mq_max_messages = int(mq_max_messages)
+
+ if not mq_max_messages:
+ # We're on a non-Linux, non-BSD system, or OS X, or BSD with
+ # the mqueuefs kernel module not loaded (which it's not, by default,
+ # under FreeBSD 8.x and 9.x. which are the only systems I've tested).
+ #
+ # If we're on FreeBSD and mqueuefs isn't loaded when this code runs,
+ # sysctl won't be able to provide mq_max_messages to me. (I assume other
+ # BSDs behave the same.) If I use too large of a default, then every
+ # attempt to create a message queue via posix_ipc will fail with
+ # "ValueError: Invalid parameter(s)" unless the user explicitly sets
+ # the max_messages param.
+ if platform.system().endswith("BSD"):
+ # 100 is the value I see under FreeBSD 9.2. I hope this works
+ # elsewhere!
+ mq_max_messages = 100
+ else:
+ # We're on a non-Linux, non-BSD system. I take a wild guess at an
+ # appropriate value. The max possible is > 2 billion, but the
+ # values used by Linux and FreeBSD suggest that a smaller default
+ # is wiser.
+ mq_max_messages = 1024
+
+ return mq_max_messages
+
+
+def sniff_mq_max_message_size_default():
+ # The max message size is not defined by POSIX.
+
+ # On most systems I've tested, msg Qs are implemented via mmap-ed files
+ # or a similar interface, so the only theoretical limits are imposed by
+ # the file system. In practice, Linux and *BSD impose some tighter limits.
+
+ # On Linux, max message size is available in a /proc file and often
+ # defaults to the value of 8192.
+
+ # On FreeBSD (and other BSDs, I assume), it's available via sysctl as
+ # kern.mqueue.maxmsgsize. On my FreeBSD 9.1 test system, it defaults to
+ # 16384.
+
+ # mqueue.h defines mq_attr.mq_msgsize as a C long, so that's
+ # a practical limit for this value.
+
+ # Further complicating things is the fact that the module has to allocate
+ # a buffer the size of the queue's max message every time receive() is
+ # called, so it would be a bad idea to set this default to the max.
+ # I set it to 8192 -- not too small, not too big. I only set it smaller
+ # if I'm on a system that tells me I must do so.
+ DEFAULT = 8192
+ mq_max_message_size_default = 0
+
+ # Try to get the value from where Linux stores it.
+ try:
+ mq_max_message_size_default = \
+ int(open("/proc/sys/fs/mqueue/msgsize_max").read())
+ except:
+ # oh well
+ pass
+
+ if not mq_max_message_size_default:
+ # Maybe we're on BSD.
+ mq_max_message_size_default = get_sysctl_value('kern.mqueue.maxmsgsize')
+ if mq_max_message_size_default:
+ mq_max_message_size_default = int(mq_max_message_size_default)
+
+ if not mq_max_message_size_default:
+ mq_max_message_size_default = DEFAULT
+
+ return mq_max_message_size_default
+
+
+
+def probe():
+ linker_options = ""
+ d = { }
+
+ f = open("VERSION")
+ d["POSIX_IPC_VERSION"] = '"%s"' % f.read().strip()
+ f.close()
+
+ # Sniffing of the realtime libs has to go early in the list so as
+ # to provide correct linker options to the rest of the tests.
+ if "Darwin" in platform.uname():
+ # I skip the test under Darwin/OS X for two reasons. First, I know
+ # it isn't needed there. Second, I can't even compile the test for
+ # the realtime lib because it references mq_unlink() which OS X
+ # doesn't support. Unfortunately sniff_realtime_lib.c *must*
+ # reference mq_unlink() or some other mq_xxx() function because
+ # it is only the message queues that need the realtime libs under
+ # FreeBSD.
+ realtime_lib_is_needed = False
+ else:
+ # Some platforms (e.g. Linux & OpenSuse) require linking to librt
+ realtime_lib_is_needed = sniff_realtime_lib()
+
+ if realtime_lib_is_needed:
+ d["REALTIME_LIB_IS_NEEDED"] = ""
+ linker_options = " -lrt "
+
+ d["PAGE_SIZE"] = sniff_page_size()
+
+ if sniff_sem_getvalue(linker_options):
+ d["SEM_GETVALUE_EXISTS"] = ""
+
+ if ("SEM_GETVALUE_EXISTS" in d) and ("Darwin" in platform.uname()):
+ # sem_getvalue() isn't available on OS X. The function exists but
+ # always returns -1 (under OS X 10.9) or ENOSYS ("Function not
+ # implemented") under some earlier version(s).
+ del d["SEM_GETVALUE_EXISTS"]
+
+ if sniff_sem_timedwait(linker_options):
+ d["SEM_TIMEDWAIT_EXISTS"] = ""
+
+ d["SEM_VALUE_MAX"] = sniff_sem_value_max()
+ # A return of None means that I don't need to #define this myself.
+ if d["SEM_VALUE_MAX"] is None:
+ del d["SEM_VALUE_MAX"]
+
+ if sniff_mq_existence(linker_options):
+ d["MESSAGE_QUEUE_SUPPORT_EXISTS"] = ""
+
+ d["QUEUE_MESSAGES_MAX_DEFAULT"] = sniff_mq_max_messages()
+ d["QUEUE_MESSAGE_SIZE_MAX_DEFAULT"] = sniff_mq_max_message_size_default()
+ d["QUEUE_PRIORITY_MAX"] = sniff_mq_prio_max()
+
+ if PY_MAJOR_VERSION == 2:
+ # I only need this for Python 2.x
+ d["PY_INT_MAX"] = sys.maxint
+
+
+ msg = """/*
+This header file was generated when you ran setup. Once created, the setup
+process won't overwrite it, so you can adjust the values by hand and
+recompile if you need to.
+
+On your platform, this file may contain only this comment -- that's OK!
+
+To recreate this file, just delete it and re-run setup.py.
+*/
+
+"""
+ filename = "probe_results.h"
+ if not os.path.exists(filename):
+ lines = ["#define %s\t\t%s" % (key, d[key]) for key in d if key != "PAGE_SIZE"]
+
+ # PAGE_SIZE gets some special treatment. It's defined in header files
+ # on some systems in which case I might get a redefinition error in
+ # my header file, so I wrap it in #ifndef/#endif.
+
+ lines.append("#ifndef PAGE_SIZE")
+ lines.append("#define PAGE_SIZE\t\t%s" % d["PAGE_SIZE"])
+ lines.append("#endif")
+
+ # A trailing '\n' keeps compilers happy...
+ open(filename, "w").write(msg + '\n'.join(lines) + '\n')
+
+ return d
+
+
+if __name__ == "__main__":
+ print (probe())
+
+
+