diff options
author | Lorry Tar Creator <lorry-tar-importer@baserock.org> | 2014-02-21 04:46:49 +0000 |
---|---|---|
committer | <> | 2014-08-04 21:38:17 +0000 |
commit | f3765db04b903b3671733e07cf1541a51966dd14 (patch) | |
tree | defcc3c47d9b8bd78b97dcc04ee779a758d37b1c /prober.py | |
download | posix-ipc-tarball-f3765db04b903b3671733e07cf1541a51966dd14.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.py | 427 |
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()) + + + |