sysv_ipc-0.6.8/0000755000076500000240000000000012404600066013106 5ustar mestaff00000000000000sysv_ipc-0.6.8/common.c0000644000076500000240000000610612367006604014553 0ustar mestaff00000000000000#include "Python.h" #include "structmember.h" #include "common.h" #include key_t get_random_key(void) { int key; /* ****************************************************************** The inability to know the range of a key_t requires careful code here. Remember that KEY_MIN and KEY_MAX refer only to the limits inherent in the variable type I use internally when turning a key into a Python object and vice versa. Those limits may exceed the operating system's limits of key_t. For instance, if key_t is typedef-ed as uint, I should generate a key where 0 <= key <= UINT_MAX. Since I can't know what key_t is typedef-ed as, I take a conservative approach and generate only keys where 1 <= key <= SHRT_MAX. Such values will work if key_t is typedef-ed as a short, int, uint, long or ulong. ****************************************************************** */ do { // ref: http://www.c-faq.com/lib/randrange.html key = ((int)((double)rand() / ((double)RAND_MAX + 1) * (SHRT_MAX - 1))) + 1; } while (key == IPC_PRIVATE); return (key_t)key; } #if PY_MAJOR_VERSION < 3 PyObject * py_int_or_long_from_ulong(unsigned long value) { // Python ints are guaranteed to accept up to LONG_MAX. Anything // larger needs to be a Python long. if (value > LONG_MAX) return PyLong_FromUnsignedLong(value); else return PyInt_FromLong(value); } #endif int convert_key_param(PyObject *py_key, void *converted_key) { // Converts a PyObject into a key if possible. Returns 0 on failure. // The converted_key param should point to a NoneableKey type. // None is an acceptable key, in which case converted_key->is_none // is non-zero and converted_key->value is undefined. int rc = 0; long key = 0; ((NoneableKey *)converted_key)->is_none = 0; if (py_key == Py_None) { rc = 1; ((NoneableKey *)converted_key)->is_none = 1; } #if PY_MAJOR_VERSION < 3 else if (PyInt_Check(py_key)) { rc = 1; key = PyInt_AsLong(py_key); } #endif else if (PyLong_Check(py_key)) { rc = 1; key = PyLong_AsLong(py_key); if (PyErr_Occurred()) { // This happens when the Python long is too big for a C long. rc = 0; PyErr_Format(PyExc_ValueError, "Key must be between %ld (KEY_MIN) and %ld (KEY_MAX)", KEY_MIN, KEY_MAX); } } if (rc) { // Param is OK if (! ((NoneableKey *)converted_key)->is_none) { // It's not None; ensure it is in range if ((key >= KEY_MIN) && (key <= KEY_MAX)) ((NoneableKey *)converted_key)->value = (key_t)key; else { rc = 0; PyErr_Format(PyExc_ValueError, "Key must be between %ld (KEY_MIN) and %ld (KEY_MAX)", KEY_MIN, KEY_MAX); } } } else PyErr_SetString(PyExc_TypeError, "Key must be an integer or None"); return rc; } sysv_ipc-0.6.8/common.h0000644000076500000240000001742112367005505014561 0ustar mestaff00000000000000#include "Python.h" #include "structmember.h" #if PY_MAJOR_VERSION >= 0x02050000 #define PY_SSIZE_T_CLEAN #define PY_STRING_LENGTH_MAX PY_SSIZE_T #else #define PY_STRING_LENGTH_MAX INT_MAX #endif // define Py_TYPE for versions before Python 2.6 #ifndef Py_TYPE #define Py_TYPE(ob) (((PyObject*)(ob))->ob_type) #endif // define PyVarObject_HEAD_INIT for versions before Python 2.6 #ifndef PyVarObject_HEAD_INIT #define PyVarObject_HEAD_INIT(type, size) PyObject_HEAD_INIT(type) size, #endif /* In sys/ipc.h under OS X, I get stuck with the old, "do not use" version of the ipc_perm struct. This is unavoidable because Python.h #undefs _POSIX_C_SOURCE and _XOPEN_SOURCE. This causes cdefs.h to #define __DARWIN_UNIX03 to 0 (implying the "default" compilation environment, as opposed to "strict") and therefore I get the crappy, old version of the struct. I don't think there's any way around this unless Python changes its headers. */ #include #include #include #include #include "probe_results.h" /* Struct to contain a key which can be None */ typedef struct { int is_none; key_t value; } NoneableKey; /* These identifiers are prefixed with SVIFP_ which stands for SysV Ipc For Python. It's really just a random string of letters to prevent clashes with other constants (as happens with SHM_SIZE on AIX). */ enum GET_SET_IDENTIFIERS { SVIFP_IPC_PERM_UID = 1, SVIFP_IPC_PERM_GID, SVIFP_IPC_PERM_CUID, SVIFP_IPC_PERM_CGID, SVIFP_IPC_PERM_MODE, SVIFP_SEM_OTIME, SVIFP_SHM_SIZE, SVIFP_SHM_LAST_ATTACH_TIME, SVIFP_SHM_LAST_DETACH_TIME, SVIFP_SHM_LAST_CHANGE_TIME, SVIFP_SHM_CREATOR_PID, SVIFP_SHM_LAST_AT_DT_PID, SVIFP_SHM_NUMBER_ATTACHED, SVIFP_MQ_LAST_SEND_TIME, SVIFP_MQ_LAST_RECEIVE_TIME, SVIFP_MQ_LAST_CHANGE_TIME, SVIFP_MQ_CURRENT_MESSAGES, SVIFP_MQ_QUEUE_BYTES_MAX, SVIFP_MQ_LAST_SEND_PID, SVIFP_MQ_LAST_RECEIVE_PID }; // Shorthand for lazy typists like me #define IPC_CREX (IPC_CREAT | IPC_EXCL) #ifdef SYSV_IPC_DEBUG #define DPRINTF(fmt, args...) fprintf(stderr, "+++ " fmt, ## args) #else #define DPRINTF(fmt, args...) #endif /* ************************************************************************** I have to do some guessing about types, mostly with regard to key_t. Since I schlep these values back and forth between C and Python, I need to know approximately what types these represent so that I can call the appropriate Python function to convert them to Python values (e.g. PyInt_FromLong(), PyFloat_FromDouble(), etc.) For most of these types the SUSv3 specification states that they're integer-ish which means I can stuff them into a long and not worry. The macros below safely convert each type and above each macro I document why my type assumptions are safe. Unfortunately, key_t is an exception. The SUSv3 specification doesn't get more detailed than saying it is "arithmetic", which includes signed and unsigned short, int, long, long long, float and double (but thankfully excludes pointers). I feel I can safely ignore long long since support for it is far from ubiquitous. Ideally I would cast key_t to double and call PyWhatever_FromDouble() when sending values to Python. Representing it as a double would ensure no data loss regardless of whether it is typedef-ed as int, long, float, etc. In practice, this would be awkward. I have yet to see a platform where key_t is not integer-ish, so most or all users of this library would be surprised if it returned keys as floats. So I store keys as key_t types and cast them to long when Python forces me to be specific. The two disadvantages to this are, (1) if any platforms typedef key_t as a float or double, this code will break, and (2) KEY_MIN and KEY_MAX don't represent the real min and max of keys. Point #1 is mitigated somewhat because if any OS typedefs key_t as float or double, this code should complain loudly during compilation. Typedefs of key_t (for 32 bit systems): OpenSolaris 2008.11 - int OS X 10.5.8 - __int32_t (int) Ubuntu 9.04 - int Freebsd - l_int (int) ************************************************************************** */ // key_t is guaranteed to be an arithmetic type. Some earlier versions // of the standard didn't guarantee that it was arithmetic; the standard was // changed to guarantee that it *is* arithmetic. // ref: http://pubs.opengroup.org/onlinepubs/009696899/functions/xsh_chap02_12.html // I assume it is a long; see comment above. // Some functions return (key_t)-1, so I guess this has to be a signed type. // ref: http://www.opengroup.org/austin/interps/doc.tpl?gdid=6226 #if PY_MAJOR_VERSION > 2 #define KEY_T_TO_PY(key) PyLong_FromLong(key) #else #define KEY_T_TO_PY(key) PyInt_FromLong(key) #endif // SUSv3 guarantees a uid_t to be an integer type. Some functions return // (uid_t)-1, so I guess this has to be a signed type. // ref: http://www.opengroup.org/onlinepubs/009695399/basedefs/sys/types.h.html // ref: http://www.opengroup.org/onlinepubs/9699919799/functions/chown.html #if PY_MAJOR_VERSION > 2 #define UID_T_TO_PY(uid) PyLong_FromLong(uid) #else #define UID_T_TO_PY(uid) PyInt_FromLong(uid) #endif // SUSv3 guarantees a gid_t to be an integer type. Some functions return // (gid_t)-1, so I guess this has to be a signed type. // ref: http://www.opengroup.org/onlinepubs/009695399/basedefs/sys/types.h.html #if PY_MAJOR_VERSION > 2 #define GID_T_TO_PY(gid) PyLong_FromLong(gid) #else #define GID_T_TO_PY(gid) PyInt_FromLong(gid) #endif // I'm not sure what guarantees SUSv3 makes about a mode_t, but param 3 of // shmget() is an int that contains flags in addition to the mode, so // mode must be able to fit into an int. // ref: http://www.opengroup.org/onlinepubs/009695399/functions/shmget.html #if PY_MAJOR_VERSION > 2 #define MODE_T_TO_PY(mode) PyLong_FromLong(mode) #else #define MODE_T_TO_PY(mode) PyInt_FromLong(mode) #endif // I'm not sure what guarantees SUSv3 makes about a time_t, but the times // I deal with here are all guaranteed to be after 1 Jan 1970 which means // they'll always be positive numbers. A ulong sounds appropriate to me, // and Python agrees in posixmodule.c. #if PY_MAJOR_VERSION > 2 #define TIME_T_TO_PY(time) PyLong_FromUnsignedLong(time) #else #define TIME_T_TO_PY(time) py_int_or_long_from_ulong(time) #endif // C89 guarantees a size_t to be unsigned and fit into a ulong or smaller. #if PY_MAJOR_VERSION > 2 #define SIZE_T_TO_PY(size) PyLong_FromUnsignedLong(size) #else #define SIZE_T_TO_PY(size) py_int_or_long_from_ulong(size) #endif // SUSv3 guarantees a pid_t to be a signed integer type. Some functions // return (pid_t)-1 so I guess this has to be signed. // ref: http://www.opengroup.org/onlinepubs/000095399/basedefs/sys/types.h.html#tag_13_67 #if PY_MAJOR_VERSION > 2 #define PID_T_TO_PY(pid) PyLong_FromLong(pid) #else #define PID_T_TO_PY(pid) PyInt_FromLong(pid) #endif // The SUS guarantees a msglen_t to be an unsigned integer type. // Ditto: msgqnum_t. // ref: http://www.opengroup.org/onlinepubs/000095399/basedefs/sys/msg.h.html #if PY_MAJOR_VERSION > 2 #define MSGLEN_T_TO_PY(msglen) PyLong_FromUnsignedLong(msglen) #define MSGQNUM_T_TO_PY(msgqnum) PyLong_FromUnsignedLong(msgqnum) #else #define MSGLEN_T_TO_PY(msglen) py_int_or_long_from_ulong(msglen) #define MSGQNUM_T_TO_PY(msgqnum) py_int_or_long_from_ulong(msgqnum) #endif /* Utility functions */ key_t get_random_key(void); int convert_key_param(PyObject *, void *); #if PY_MAJOR_VERSION < 3 PyObject *py_int_or_long_from_ulong(unsigned long); #endif /* Custom Exceptions/Errors */ extern PyObject *pBaseException; extern PyObject *pInternalException; extern PyObject *pPermissionsException; extern PyObject *pExistentialException; extern PyObject *pBusyException; extern PyObject *pNotAttachedException; sysv_ipc-0.6.8/demo/0000755000076500000240000000000012404600066014032 5ustar mestaff00000000000000sysv_ipc-0.6.8/demo/cleanup.py0000644000076500000240000000070412224412321016027 0ustar mestaff00000000000000# 3rd party modules import sysv_ipc # Modules for this project import utils params = utils.read_params() try: semaphore = sysv_ipc.Semaphore(params["KEY"], 0) except: semaphore = None if semaphore: semaphore.remove() print ("The semaphore is cleaned up.") try: memory = sysv_ipc.SharedMemory(params["KEY"], 0) except: memory = None if memory: memory.remove() print ("The shared memory is cleaned up.") sysv_ipc-0.6.8/demo/conclusion.c0000644000076500000240000001166212224412321016353 0ustar mestaff00000000000000#include /* for system's IPC_xxx definitions */ #include /* for shmget, shmat, shmdt, shmctl */ #include /* for semget, semctl, semop */ #include #include #include #include #include #include "md5.h" #include "utils.h" static const char MY_NAME[] = "Mrs. Conclusion"; // Set up a Mrs. Premise & Mrs. Conclusion conversation. int main() { int sem_id = 0; int shm_id = 0; int rc; char s[1024]; int i; int done; char last_message_i_wrote[256]; char md5ified_message[256]; void *address = NULL; struct param_struct params; say(MY_NAME, "Oooo 'ello, I'm Mrs. Conclusion!"); read_params(¶ms); // Mrs. Premise has already created the semaphore and shared memory. // I just need to get handles to them. sem_id = semget(params.key, 0, params.permissions); if (-1 == sem_id) { sem_id = 0; sprintf(s, "Getting a handle to the semaphore failed; errno is %d", errno); say(MY_NAME, s); } else { // get a handle to the shared memory shm_id = shmget(params.key, params.size, params.permissions); if (shm_id == -1) { shm_id = 0; sprintf(s, "Couldn't get a handle to the shared memory; errno is %d", errno); say(MY_NAME, s); } else { sprintf(s, "Shared memory's id is %d", shm_id); say(MY_NAME, s); // Attach the memory. address = shmat(shm_id, NULL, 0); if ((void *)-1 == address) { address = NULL; sprintf(s, "Attaching the shared memory failed; errno is %d", errno); say(MY_NAME, s); } else { sprintf(s, "shared memory address = %p", address); say(MY_NAME, s); i = 0; done = 0; last_message_i_wrote[0] = '\0'; while (!done) { sprintf(s, "iteration %d", i); say(MY_NAME, s); // Wait for Mrs. Premise to free up the semaphore. rc = acquire_semaphore(MY_NAME, sem_id, params.live_dangerously); if (rc) done = 1; else { while ( (!rc) && \ (!strcmp((char *)address, last_message_i_wrote)) ) { // Nothing new; give Mrs. Premise another change to respond. sprintf(s, "Read %zu characters '%s'", strlen((char *)address), (char *)address); say(MY_NAME, s); rc = release_semaphore(MY_NAME, sem_id, params.live_dangerously); if (!rc) { rc = acquire_semaphore(MY_NAME, sem_id, params.live_dangerously); } } md5ify(last_message_i_wrote, md5ified_message); // I always accept the first message (when i == 0) if ( (i == 0) || (!strcmp(md5ified_message, (char *)address)) ) { // All is well i++; if (i == params.iterations) done = 1; // MD5 the reply and write back to Mrs. Premise. md5ify((char *)address, md5ified_message); // Write back to Mrs. Premise. sprintf(s, "Writing %zu characters '%s'", strlen(md5ified_message), md5ified_message); say(MY_NAME, s); strcpy((char *)address, md5ified_message); strcpy(last_message_i_wrote, md5ified_message); } else { sprintf(s, "Shared memory corruption after %d iterations.", i); say(MY_NAME, s); sprintf(s, "Mismatch; rc = %d, new message is '%s', expected '%s'.", rc, (char *)address, md5ified_message); say(MY_NAME, s); done = 1; } } // Release the semaphore. rc = release_semaphore(MY_NAME, sem_id, params.live_dangerously); if (rc) done = 1; } if (-1 == shmdt(address)) { sprintf(s, "Detaching the memory failed; errno is %d", errno); say(MY_NAME, s); } address = NULL; } } } return 0; } sysv_ipc-0.6.8/demo/conclusion.py0000755000076500000240000000376412224412321016570 0ustar mestaff00000000000000# Python modules import os import sys PY_MAJOR_VERSION = sys.version_info[0] # hashlib is only available in Python >= 2.5. I still want to support # older Pythons so I import md5 if hashlib is not available. Fortunately # md5 can masquerade as hashlib for my purposes. try: import hashlib except ImportError: import md5 as hashlib # 3rd party modules import sysv_ipc # Utils for this demo import utils if PY_MAJOR_VERSION > 2: import utils_for_3 as flex_utils else: import utils_for_2 as flex_utils utils.say("Oooo 'ello, I'm Mrs. Conclusion!") params = utils.read_params() semaphore = sysv_ipc.Semaphore(params["KEY"]) memory = sysv_ipc.SharedMemory(params["KEY"]) utils.say("memory attached at %d" % memory.address) what_i_wrote = "" s = "" for i in range(0, params["ITERATIONS"]): utils.say("i = %d" % i) if not params["LIVE_DANGEROUSLY"]: # Wait for Mrs. Premise to free up the semaphore. utils.say("acquiring the semaphore...") semaphore.acquire() s = utils.read_from_memory(memory) while s == what_i_wrote: if not params["LIVE_DANGEROUSLY"]: # Release the semaphore... utils.say("releasing the semaphore") semaphore.release() # ...and wait for it to become available again. utils.say("acquiring for the semaphore...") semaphore.acquire() s = utils.read_from_memory(memory) if what_i_wrote: if PY_MAJOR_VERSION > 2: what_i_wrote = what_i_wrote.encode() try: assert(s == hashlib.md5(what_i_wrote).hexdigest()) except AssertionError: flex_utils.raise_error(AssertionError, "Shared memory corruption after %d iterations." % i) if PY_MAJOR_VERSION > 2: s = s.encode() what_i_wrote = hashlib.md5(s).hexdigest() utils.write_to_memory(memory, what_i_wrote) if not params["LIVE_DANGEROUSLY"]: utils.say("releasing the semaphore") semaphore.release() sysv_ipc-0.6.8/demo/make_all.sh0000755000076500000240000000027212224412321016132 0ustar mestaff00000000000000#!/usr/bin/env bash gcc -Wall -c -o md5.o md5.c gcc -Wall -c -o utils.o utils.c gcc -Wall -L. md5.o utils.o -o premise premise.c gcc -Wall -L. md5.o utils.o -o conclusion conclusion.c sysv_ipc-0.6.8/demo/md5.c0000644000076500000240000003022212224412321014655 0ustar mestaff00000000000000/* Copyright (C) 1999, 2000, 2002 Aladdin Enterprises. All rights reserved. This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. L. Peter Deutsch ghost@aladdin.com */ /* $Id: md5.c,v 1.6 2002/04/13 19:20:28 lpd Exp $ */ /* Independent implementation of MD5 (RFC 1321). This code implements the MD5 Algorithm defined in RFC 1321, whose text is available at http://www.ietf.org/rfc/rfc1321.txt The code is derived from the text of the RFC, including the test suite (section A.5) but excluding the rest of Appendix A. It does not include any code or documentation that is identified in the RFC as being copyrighted. The original and principal author of md5.c is L. Peter Deutsch . Other authors are noted in the change history that follows (in reverse chronological order): 2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order either statically or dynamically; added missing #include in library. 2002-03-11 lpd Corrected argument list for main(), and added int return type, in test program and T value program. 2002-02-21 lpd Added missing #include in test program. 2000-07-03 lpd Patched to eliminate warnings about "constant is unsigned in ANSI C, signed in traditional"; made test program self-checking. 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5). 1999-05-03 lpd Original version. */ #include "md5.h" #include #undef BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */ #ifdef ARCH_IS_BIG_ENDIAN # define BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1) #else # define BYTE_ORDER 0 #endif #define T_MASK ((md5_word_t)~0) #define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87) #define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9) #define T3 0x242070db #define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111) #define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050) #define T6 0x4787c62a #define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec) #define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe) #define T9 0x698098d8 #define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850) #define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e) #define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841) #define T13 0x6b901122 #define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c) #define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71) #define T16 0x49b40821 #define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d) #define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf) #define T19 0x265e5a51 #define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855) #define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2) #define T22 0x02441453 #define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e) #define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437) #define T25 0x21e1cde6 #define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829) #define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278) #define T28 0x455a14ed #define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa) #define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07) #define T31 0x676f02d9 #define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375) #define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd) #define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e) #define T35 0x6d9d6122 #define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3) #define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb) #define T38 0x4bdecfa9 #define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f) #define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f) #define T41 0x289b7ec6 #define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805) #define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a) #define T44 0x04881d05 #define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6) #define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a) #define T47 0x1fa27cf8 #define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a) #define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb) #define T50 0x432aff97 #define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58) #define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6) #define T53 0x655b59c3 #define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d) #define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82) #define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e) #define T57 0x6fa87e4f #define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f) #define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb) #define T60 0x4e0811a1 #define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d) #define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca) #define T63 0x2ad7d2bb #define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e) static void md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/) { md5_word_t a = pms->abcd[0], b = pms->abcd[1], c = pms->abcd[2], d = pms->abcd[3]; md5_word_t t; #if BYTE_ORDER > 0 /* Define storage only for big-endian CPUs. */ md5_word_t X[16]; #else /* Define storage for little-endian or both types of CPUs. */ md5_word_t xbuf[16]; const md5_word_t *X; #endif { #if BYTE_ORDER == 0 /* * Determine dynamically whether this is a big-endian or * little-endian machine, since we can use a more efficient * algorithm on the latter. */ static const int w = 1; if (*((const md5_byte_t *)&w)) /* dynamic little-endian */ #endif #if BYTE_ORDER <= 0 /* little-endian */ { /* * On little-endian machines, we can process properly aligned * data without copying it. */ if (!((data - (const md5_byte_t *)0) & 3)) { /* data are properly aligned */ X = (const md5_word_t *)data; } else { /* not aligned */ memcpy(xbuf, data, 64); X = xbuf; } } #endif #if BYTE_ORDER == 0 else /* dynamic big-endian */ #endif #if BYTE_ORDER >= 0 /* big-endian */ { /* * On big-endian machines, we must arrange the bytes in the * right order. */ const md5_byte_t *xp = data; int i; # if BYTE_ORDER == 0 X = xbuf; /* (dynamic only) */ # else # define xbuf X /* (static only) */ # endif for (i = 0; i < 16; ++i, xp += 4) xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24); } #endif } #define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) /* Round 1. */ /* Let [abcd k s i] denote the operation a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */ #define F(x, y, z) (((x) & (y)) | (~(x) & (z))) #define SET(a, b, c, d, k, s, Ti)\ t = a + F(b,c,d) + X[k] + Ti;\ a = ROTATE_LEFT(t, s) + b /* Do the following 16 operations. */ SET(a, b, c, d, 0, 7, T1); SET(d, a, b, c, 1, 12, T2); SET(c, d, a, b, 2, 17, T3); SET(b, c, d, a, 3, 22, T4); SET(a, b, c, d, 4, 7, T5); SET(d, a, b, c, 5, 12, T6); SET(c, d, a, b, 6, 17, T7); SET(b, c, d, a, 7, 22, T8); SET(a, b, c, d, 8, 7, T9); SET(d, a, b, c, 9, 12, T10); SET(c, d, a, b, 10, 17, T11); SET(b, c, d, a, 11, 22, T12); SET(a, b, c, d, 12, 7, T13); SET(d, a, b, c, 13, 12, T14); SET(c, d, a, b, 14, 17, T15); SET(b, c, d, a, 15, 22, T16); #undef SET /* Round 2. */ /* Let [abcd k s i] denote the operation a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */ #define G(x, y, z) (((x) & (z)) | ((y) & ~(z))) #define SET(a, b, c, d, k, s, Ti)\ t = a + G(b,c,d) + X[k] + Ti;\ a = ROTATE_LEFT(t, s) + b /* Do the following 16 operations. */ SET(a, b, c, d, 1, 5, T17); SET(d, a, b, c, 6, 9, T18); SET(c, d, a, b, 11, 14, T19); SET(b, c, d, a, 0, 20, T20); SET(a, b, c, d, 5, 5, T21); SET(d, a, b, c, 10, 9, T22); SET(c, d, a, b, 15, 14, T23); SET(b, c, d, a, 4, 20, T24); SET(a, b, c, d, 9, 5, T25); SET(d, a, b, c, 14, 9, T26); SET(c, d, a, b, 3, 14, T27); SET(b, c, d, a, 8, 20, T28); SET(a, b, c, d, 13, 5, T29); SET(d, a, b, c, 2, 9, T30); SET(c, d, a, b, 7, 14, T31); SET(b, c, d, a, 12, 20, T32); #undef SET /* Round 3. */ /* Let [abcd k s t] denote the operation a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */ #define H(x, y, z) ((x) ^ (y) ^ (z)) #define SET(a, b, c, d, k, s, Ti)\ t = a + H(b,c,d) + X[k] + Ti;\ a = ROTATE_LEFT(t, s) + b /* Do the following 16 operations. */ SET(a, b, c, d, 5, 4, T33); SET(d, a, b, c, 8, 11, T34); SET(c, d, a, b, 11, 16, T35); SET(b, c, d, a, 14, 23, T36); SET(a, b, c, d, 1, 4, T37); SET(d, a, b, c, 4, 11, T38); SET(c, d, a, b, 7, 16, T39); SET(b, c, d, a, 10, 23, T40); SET(a, b, c, d, 13, 4, T41); SET(d, a, b, c, 0, 11, T42); SET(c, d, a, b, 3, 16, T43); SET(b, c, d, a, 6, 23, T44); SET(a, b, c, d, 9, 4, T45); SET(d, a, b, c, 12, 11, T46); SET(c, d, a, b, 15, 16, T47); SET(b, c, d, a, 2, 23, T48); #undef SET /* Round 4. */ /* Let [abcd k s t] denote the operation a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */ #define I(x, y, z) ((y) ^ ((x) | ~(z))) #define SET(a, b, c, d, k, s, Ti)\ t = a + I(b,c,d) + X[k] + Ti;\ a = ROTATE_LEFT(t, s) + b /* Do the following 16 operations. */ SET(a, b, c, d, 0, 6, T49); SET(d, a, b, c, 7, 10, T50); SET(c, d, a, b, 14, 15, T51); SET(b, c, d, a, 5, 21, T52); SET(a, b, c, d, 12, 6, T53); SET(d, a, b, c, 3, 10, T54); SET(c, d, a, b, 10, 15, T55); SET(b, c, d, a, 1, 21, T56); SET(a, b, c, d, 8, 6, T57); SET(d, a, b, c, 15, 10, T58); SET(c, d, a, b, 6, 15, T59); SET(b, c, d, a, 13, 21, T60); SET(a, b, c, d, 4, 6, T61); SET(d, a, b, c, 11, 10, T62); SET(c, d, a, b, 2, 15, T63); SET(b, c, d, a, 9, 21, T64); #undef SET /* Then perform the following additions. (That is increment each of the four registers by the value it had before this block was started.) */ pms->abcd[0] += a; pms->abcd[1] += b; pms->abcd[2] += c; pms->abcd[3] += d; } void md5_init(md5_state_t *pms) { pms->count[0] = pms->count[1] = 0; pms->abcd[0] = 0x67452301; pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476; pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301; pms->abcd[3] = 0x10325476; } void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes) { const md5_byte_t *p = data; int left = nbytes; int offset = (pms->count[0] >> 3) & 63; md5_word_t nbits = (md5_word_t)(nbytes << 3); if (nbytes <= 0) return; /* Update the message length. */ pms->count[1] += nbytes >> 29; pms->count[0] += nbits; if (pms->count[0] < nbits) pms->count[1]++; /* Process an initial partial block. */ if (offset) { int copy = (offset + nbytes > 64 ? 64 - offset : nbytes); memcpy(pms->buf + offset, p, copy); if (offset + copy < 64) return; p += copy; left -= copy; md5_process(pms, pms->buf); } /* Process full blocks. */ for (; left >= 64; p += 64, left -= 64) md5_process(pms, p); /* Process a final partial block. */ if (left) memcpy(pms->buf, p, left); } void md5_finish(md5_state_t *pms, md5_byte_t digest[16]) { static const md5_byte_t pad[64] = { 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; md5_byte_t data[8]; int i; /* Save the length before padding. */ for (i = 0; i < 8; ++i) data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3)); /* Pad to 56 bytes mod 64. */ md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1); /* Append the length. */ md5_append(pms, data, 8); for (i = 0; i < 16; ++i) digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3)); } sysv_ipc-0.6.8/demo/md5.h0000644000076500000240000000650012224412321014664 0ustar mestaff00000000000000/* Copyright (C) 1999, 2002 Aladdin Enterprises. All rights reserved. This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. L. Peter Deutsch ghost@aladdin.com */ /* $Id: md5.h,v 1.4 2002/04/13 19:20:28 lpd Exp $ */ /* Independent implementation of MD5 (RFC 1321). This code implements the MD5 Algorithm defined in RFC 1321, whose text is available at http://www.ietf.org/rfc/rfc1321.txt The code is derived from the text of the RFC, including the test suite (section A.5) but excluding the rest of Appendix A. It does not include any code or documentation that is identified in the RFC as being copyrighted. The original and principal author of md5.h is L. Peter Deutsch . Other authors are noted in the change history that follows (in reverse chronological order): 2002-04-13 lpd Removed support for non-ANSI compilers; removed references to Ghostscript; clarified derivation from RFC 1321; now handles byte order either statically or dynamically. 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5); added conditionalization for C++ compilation from Martin Purschke . 1999-05-03 lpd Original version. */ #ifndef md5_INCLUDED # define md5_INCLUDED /* * This package supports both compile-time and run-time determination of CPU * byte order. If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be * compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is * defined as non-zero, the code will be compiled to run only on big-endian * CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to * run on either big- or little-endian CPUs, but will run slightly less * efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined. */ typedef unsigned char md5_byte_t; /* 8-bit byte */ typedef unsigned int md5_word_t; /* 32-bit word */ /* Define the state of the MD5 Algorithm. */ typedef struct md5_state_s { md5_word_t count[2]; /* message length in bits, lsw first */ md5_word_t abcd[4]; /* digest buffer */ md5_byte_t buf[64]; /* accumulate block */ } md5_state_t; #ifdef __cplusplus extern "C" { #endif /* Initialize the algorithm. */ void md5_init(md5_state_t *pms); /* Append a string to the message. */ void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes); /* Finish the message and return the digest. */ void md5_finish(md5_state_t *pms, md5_byte_t digest[16]); #ifdef __cplusplus } /* end extern "C" */ #endif #endif /* md5_INCLUDED */ sysv_ipc-0.6.8/demo/params.txt0000644000076500000240000000106712224412321016055 0ustar mestaff00000000000000# These parameters control how Mrs. Premise and Mrs. Conclusion behave. # ITERATIONS is the number of times they'll talk to one another. # LIVE_DANGEROUSLY is a Boolean (0 or 1); if set to 1 the programs # won't use the semaphore to coordinate access to the shared # memory. Corruption will likely result. # KEY is the key to be used for the semaphore and shared memory. # PERMISSIONS are in octal (note the leading 0). # SHM_SIZE is the size of the shared memory segment in bytes. ITERATIONS=1000 LIVE_DANGEROUSLY=0 KEY=42 PERMISSIONS=0600 SHM_SIZE=4096 sysv_ipc-0.6.8/demo/premise.c0000644000076500000240000001723412224412321015644 0ustar mestaff00000000000000#include /* for system's IPC_xxx definitions */ #include /* for shmget, shmat, shmdt, shmctl */ #include /* for semget, semctl, semop */ #include #include #include #include #include #include #include #include #include "md5.h" #include "utils.h" const char MY_NAME[] = "Mrs. Premise"; // Set up a Mrs. Premise & Mrs. Conclusion conversation. void get_current_time(char *); int main() { int rc; char s[1024]; char last_message_i_wrote[256]; char md5ified_message[256]; int i = 0; int done = 0; struct param_struct params; int shm_id; void *address = NULL; int sem_id; struct shmid_ds shm_info; say(MY_NAME, "Oooo 'ello, I'm Mrs. Premise!"); read_params(¶ms); // Create the shared memory shm_id = shmget(params.key, params.size, IPC_CREAT | IPC_EXCL | params.permissions); if (shm_id == -1) { shm_id = 0; sprintf(s, "Creating the shared memory failed; errno is %d", errno); say(MY_NAME, s); } else { sprintf(s, "Shared memory's id is %d", shm_id); say(MY_NAME, s); // Attach the memory. address = shmat(shm_id, NULL, 0); if ((void *)-1 == address) { address = NULL; sprintf(s, "Attaching the shared memory failed; errno is %d", errno); say(MY_NAME, s); } else { sprintf(s, "shared memory address = %p", address); say(MY_NAME, s); } } if (address) { // Create the semaphore sem_id = semget(params.key, 1, IPC_CREAT | IPC_EXCL | params.permissions); if (-1 == sem_id) { sem_id = 0; sprintf(s, "Creating the semaphore failed; errno is %d", errno); say(MY_NAME, s); } else { sprintf(s, "the semaphore id is %d", sem_id); say(MY_NAME, s); // I seed the shared memory with a random string (the current time). get_current_time(s); strcpy((char *)address, s); strcpy(last_message_i_wrote, s); sprintf(s, "Wrote %zu characters: %s", strlen(last_message_i_wrote), last_message_i_wrote); say(MY_NAME, s); i = 0; while (!done) { sprintf(s, "iteration %d", i); say(MY_NAME, s); // Release the semaphore... rc = release_semaphore(MY_NAME, sem_id, params.live_dangerously); // ...and wait for it to become available again. In real code // I might want to sleep briefly before calling .acquire() in // order to politely give other processes an opportunity to grab // the semaphore while it is free so as to avoid starvation. But // this code is meant to be a stress test that maximizes the // opportunity for shared memory corruption and politeness is // not helpful in stress tests. if (!rc) rc = acquire_semaphore(MY_NAME, sem_id, params.live_dangerously); if (rc) done = 1; else { // I keep checking the shared memory until something new has // been written. while ( (!rc) && \ (!strcmp((char *)address, last_message_i_wrote)) ) { // Nothing new; give Mrs. Conclusion another change to respond. sprintf(s, "Read %zu characters '%s'", strlen((char *)address), (char *)address); say(MY_NAME, s); rc = release_semaphore(MY_NAME, sem_id, params.live_dangerously); if (!rc) { rc = acquire_semaphore(MY_NAME, sem_id, params.live_dangerously); } } if (rc) done = 1; else { sprintf(s, "Read %zu characters '%s'", strlen((char *)address), (char *)address); say(MY_NAME, s); // What I read must be the md5 of what I wrote or something's // gone wrong. md5ify(last_message_i_wrote, md5ified_message); if (strcmp(md5ified_message, (char *)address) == 0) { // Yes, the message is OK i++; if (i == params.iterations) done = 1; // MD5 the reply and write back to Mrs. Conclusion. md5ify(md5ified_message, md5ified_message); sprintf(s, "Writing %zu characters '%s'", strlen(md5ified_message), md5ified_message); say(MY_NAME, s); strcpy((char *)address, md5ified_message); strcpy((char *)last_message_i_wrote, md5ified_message); } else { sprintf(s, "Shared memory corruption after %d iterations.", i); say(MY_NAME, s); sprintf(s, "Mismatch; new message is '%s', expected '%s'.", (char *)address, md5ified_message); say(MY_NAME, s); done = 1; } } } } // Announce for one last time that the semaphore is free again so that // Mrs. Conclusion can exit. say(MY_NAME, "Final release of the semaphore followed by a 5 second pause"); rc = release_semaphore(MY_NAME, sem_id, params.live_dangerously); sleep(5); // ...before beginning to wait until it is free again. // Technically, this is bad practice. It's possible that on a // heavily loaded machine, Mrs. Conclusion wouldn't get a chance // to acquire the semaphore. There really ought to be a loop here // that waits for some sort of goodbye message but for purposes of // simplicity I'm skipping that. say(MY_NAME, "Final wait to acquire the semaphore"); rc = acquire_semaphore(MY_NAME, sem_id, params.live_dangerously); if (!rc) { say(MY_NAME, "Destroying the shared memory."); if (-1 == shmdt(address)) { sprintf(s, "Detaching the memory failed; errno is %d", errno); say(MY_NAME, s); } address = NULL; if (-1 == shmctl(shm_id, IPC_RMID, &shm_info)) { sprintf(s, "Removing the memory failed; errno is %d", errno); say(MY_NAME, s); } } } say(MY_NAME, "Destroying the semaphore."); // Clean up the semaphore if (-1 == semctl(sem_id, 0, IPC_RMID)) { sprintf(s, "Removing the semaphore failed; errno is %d", errno); say(MY_NAME, s); } } return 0; } void get_current_time(char *s) { time_t the_time; struct tm *the_localtime; char *pAscTime; the_time = time(NULL); the_localtime = localtime(&the_time); pAscTime = asctime(the_localtime); strcpy(s, pAscTime); } sysv_ipc-0.6.8/demo/premise.py0000755000076500000240000000753212224412321016055 0ustar mestaff00000000000000# Python modules import time import os import sys PY_MAJOR_VERSION = sys.version_info[0] # hashlib is only available in Python >= 2.5. I still want to support # older Pythons so I import md5 if hashlib is not available. Fortunately # md5 can masquerade as hashlib for my purposes. try: import hashlib except ImportError: import md5 as hashlib # 3rd party modules import sysv_ipc # Utils for this demo import utils if PY_MAJOR_VERSION > 2: import utils_for_3 as flex_utils else: import utils_for_2 as flex_utils utils.say("Oooo 'ello, I'm Mrs. Premise!") params = utils.read_params() # Create the semaphore & shared memory. I read somewhere that semaphores # and shared memory have separate key spaces, so one can safely use the # same key for each. This seems to be true in my experience. # For purposes of simplicity, this demo code makes no allowance for the # failure of the semaphore or memory constructors. This is unrealistic # because one can never predict whether or not a given key will be available, # so your code must *always* be prepared for these functions to fail. semaphore = sysv_ipc.Semaphore(params["KEY"], sysv_ipc.IPC_CREX) memory = sysv_ipc.SharedMemory(params["KEY"], sysv_ipc.IPC_CREX) # I seed the shared memory with a random value which is the current time. what_i_wrote = time.asctime() s = what_i_wrote utils.write_to_memory(memory, what_i_wrote) for i in range(0, params["ITERATIONS"]): utils.say("iteration %d" % i) if not params["LIVE_DANGEROUSLY"]: # Releasing the semaphore... utils.say("releasing the semaphore") semaphore.release() # ...and wait for it to become available again. In real code it'd be # wise to sleep briefly before calling .acquire() in order to be # polite and give other processes an opportunity to grab the semaphore # while it is free and thereby avoid starvation. But this code is meant # to be a stress test that maximizes the opportunity for shared memory # corruption, and politeness has no place in that. utils.say("acquiring the semaphore...") semaphore.acquire() s = utils.read_from_memory(memory) # I keep checking the shared memory until something new has been written. while s == what_i_wrote: if not params["LIVE_DANGEROUSLY"]: utils.say("releasing the semaphore") semaphore.release() utils.say("acquiring the semaphore...") semaphore.acquire() # Once the call to .acquire() completes, I own the shared resource and # I'm free to read from the memory. s = utils.read_from_memory(memory) # What I read must be the md5 of what I wrote or something's gone wrong. if PY_MAJOR_VERSION > 2: what_i_wrote = what_i_wrote.encode() try: assert(s == hashlib.md5(what_i_wrote).hexdigest()) except AssertionError: flex_utils.raise_error(AssertionError, "Shared memory corruption after %d iterations." % i) # MD5 the reply and write back to Mrs. Conclusion. if PY_MAJOR_VERSION > 2: s = s.encode() what_i_wrote = hashlib.md5(s).hexdigest() utils.write_to_memory(memory, what_i_wrote) # Announce for one last time that the semaphore is free again so that # Mrs. Conclusion can exit. if not params["LIVE_DANGEROUSLY"]: utils.say("Final release of the semaphore followed by a 5 second pause") semaphore.release() time.sleep(5) # ...before beginning to wait until it is free again. utils.say("Final acquisition of the semaphore") semaphore.acquire() utils.say("Destroying semaphore and shared memory") # It'd be more natural to call memory.remove() and semaphore.remove() here, # but I'll use the module-level functions instead to demonstrate their use. sysv_ipc.remove_shared_memory(memory.id) sysv_ipc.remove_semaphore(semaphore.id) sysv_ipc-0.6.8/demo/ReadMe.txt0000644000076500000240000000526412224412321015732 0ustar mestaff00000000000000This demonstrates use of shared memory and semaphores via two applications named after Mrs. Premise and Mrs. Conclusion of the Monty Python sketch. http://www.youtube.com/watch?v=crIJvcWkVcs Like those two characters, these programs chat back and forth and the result is a lot of nonsense. In this case, what the programs are saying isn't the interesting part. What's interesting is how they're doing it. Mrs. Premise and Mrs. Conclusion (the programs, not the sketch characters) communicate through IPC shared memory with a semaphore to control access to that memory. Mrs. Premise starts things off by creating the shared memory and semaphore and writing a random string (the current time) to the memory. She then sits in a loop reading the memory. If it holds the same message she wrote, she does nothing. If it is a new message, it must be from Mrs. Conclusion. Meanwhile, Mrs. Conclusion is doing exactly the same thing, except that she assumes Mrs. Premise will write the first message. When either of these programs reads a new message, they write back an md5 hash of that message. This serves two purposes. First, it ensures that subsequent messages are very different so that if a message somehow gets corrupted (say by being partially overwritten by the next message), it will not escape notice. Second, it ensures that corruption can be detected if it happens, because Mrs. Premise and Mrs. Conclusion can calculate what the other's response to their message should be. Since they use a semaphore to control access to the shared memory, Mrs. Premise and Mrs. Conclusion won't ever find their messages corrupted no matter how many messages they exchange. You can experiment with this by setting ITERATIONS in params.txt to a very large value. You can change LIVE_DANGEROUSLY (also in params.txt) to a non-zero value to tell Mrs. Premise and Mrs. Conclusion to run without using the semaphore. The shared memory will probably get corrupted in fewer than 1000 iterations. To run the demo, start Mrs. Premise first in one window and then run Mrs. Conclusion in another. The Fancy Version ================= If you want to get fancy, you can play with C versions of Mrs. Premise and Mrs. Conclusion. The script make_all.sh will compile them for you. (Linux users will need to edit the script and uncomment the line for the Linux-specific linker option.) The resulting executables are called premise and conclusion and work exactly the same as their Python counterparts. You can have the two C programs talk to one another, or you can have premise.py talk to the C version of conclusion...the possibilities are endless. (Actually, there are only four possible combinations but "endless" sounds better.) sysv_ipc-0.6.8/demo/SampleIpcConversation.png0000644000076500000240000002720512224412321021011 0ustar mestaff00000000000000PNG  IHDR'TnsRGBbKGD̿ pHYs  tIME 2) IDATx{|Lg?gfrMl"Pմ4E.E]Zj֥4(Zjז^^EؖRU֥A5( %ȍ\&33$LJdȜgs\;!W0'9! aNsBBr͘lv8'XV!XEDI+ +UEQŠ(p kbW^*,V!3ȣWpaMk'VRۣO `šD;Z,/(&[PX[ML KYg3k'n`MĤ=u,fפDvkKyxO?~9=#I3'Vc&q:{'?r@Qr@#om^k^KgP7YwjY}51NP9v 2 3[۾nh)Vn[ܷy 5kW?+^!>čx,zDp*T}lrI 8A/<૏f݁٢˩e`#;v<'n{|K::}tju0[ 57BQR;<+G\@; ܍(>F 6lrjY}5?A5tz "w&iڭb ܏>'hS{€z8D/JpAH رe}-"p|ZdVvj3q_{'{t {7Y@;K`hW4eIWul>36۰g{pjY}ܰ4ܣ,v,ձOtscNȧ:?`C DO.o?[[}2F"ma,{ԲPC73[ɖKyauv}/֬pjĆZ>F|D}lgsp2m$]Rj9˩euT3Onuk[BC r50P/ZrSÜ0'9! aNsBBrIXZ`N039)f=FtI~)t/.ڴO85 ^"iyNx6p~_3'<7sNjn׽ǧL ogYsn:׮ہm(y)6"ۜc޷ҏs71Ȏ{lQ?{&N5Μ'ÊJQfpevrn~m_)6 iS4>f6Fuq'j{RP挐 |+QCߣvt+6"q0'9!5rsW$.:hÍ9S(F6ZY.yND>Fsm~/i=t{?Q u8:uRo7"G[ӑOwH0Oim&E*ۜkT@L}b3|BKiaN֑ \n(w+sy$O<[FmPqCw!֍Crxj!8A%xiplu$|aE5%Z;yOU_iz<Q:8u oO4/B {zj AM3'2[g%3,{`󓳀S7MvWU> ?DO^~>?۵;n='!~;^^[gg h9ݻn+Ų{oA4=M+7V$oN 7Mhxlq{~$K@r/թ3srg×>? ,a,h5-ݱd"{ N{||zoz%@ݼMmFg‚aK̉b~ xtu@`؂ӇoUH,'j?E;'_Ydm`NR(#6 5؈ޟܧ%֘%\OܒÁYZ:8kjZJQϣ%땦MO_+V6m v0pp9Ft.z&%m^_:$2!yFNNOT˔|i jUp&tVpŠdS(QX;^a Vjx߾4IY_1A\+|u+tSo]+7odJty͞˅x3zD66[đ<{ W/;SS|HۺV:?q#gL^vhs4=֢,xynT(1MZq0%<̜ƣ٧D߁t[O+˜cz,YKMgNݜ~Mk}K1=4 [-oW`a(Pm.FozBwk@Edێi{J?'71Ȏ{Q?{&N5\Oj~R$mg=Q iS"mgaz@w jÜ3k&Ez@>nF}VG~5OК2qO :mX'?iQ5'~Xtƴړ %uK} kV~f-,l5NWO3ڌ]J7lUFɽ/nMhmTO}٦h6I#B3`דkDm?w&6ul { ӁW <띛>6(`ZeSnAk*ּ!t7O?GJ7l@c1|WO-MxdM#;_ZڔaOjGjdvĦSX+U}b?䩟:Z/{lM5 +P]JQǠuT\ Ӱˏa*Q_Yboy}S8nsҿs5|7[D*k`8 ϯT0'9! aNsBœœ0'9! aNœ\90'UfdJt)G޷soF҆6]aN${ WbJb* yow[rj܊~;;ir{EY.qjhDirT1MZq0%<̩aN4fgo ~an?h.9!#, ^Yb ;ϩaNv/娿&ĵ߾jf"籒bNf!0-;8{Of`;83̉?`yq0'9!OXGJ.qgzd |&VRe)S&oM=ÎlNmJP4фɭZ{*7#nt֏"]f}$sr tB`0 O5+VݼM6߾aͺ5 ЭEdz=l Vr"|02;gZAQ.׋9x @u"￰ S:iB_,Hy0};{cklVP?]T,фǺ}u"H)egp5_/~tJ6R /{S kݩ~:n6J Y]T,c㧑O zlֻt1MlP;4GJ.E=b-w}^,`Xz}5hUclw͍wbX]p=шҌ>c&_`Jz=x&]̉Ӄ\s"QA@:̭0'61X|s+sRla~95̉1ׄ۷OmABLj裾y }̉+G͜}̉KW>jeq0'9!>j"&ޡxܡ+9+9z>jb}z%.飾^œ0'9! aNsBsBœ0'9! aNaNsBbGK3'9 qS?sfęq3SpO?sn9q?QϜkP7-cqs3kl`{k|Zޮ.Qg]:׮ہm(y~\O nb@i%xޅv3!Z,]H0{ #ү1ҦD6FuQcJ}IAm3BsfppՄ'nNmYq0'9!̉ۢ׬䯪9x*bE쩲kEMT Ne4.;tZv=0'7^nD&BṪW2T1#R&UnMԇ-o+ZѫSft]])B~䇟U:;66~v.œ#7@x0ذHRkE5p! !B0WKbjX3ҝVp㋋?Jqn$6C3wE^MK13!sBLI!M㻅pTi0!rqӓ~?5zR{ @ӃEh VΫ0nwМiR٤MѥkȿoM).Y= @m+ʑ^?wN8`G/=]6tq=[!>yE!Yn8"OzoNyѷrBQkBtJqB\.%4 IDATz ͉DBNF/-]>]yGŒĺ!7- !^|T!m͘G!ıNB!Dm_;"-/ཙ\O{ p ^uS]NќiR!h.]6FIr/)ut]7{# {!G)_b6&Rpm qnc]n^&6J.@##_l7p`O~x899%yc| k֭͛S(it-,A^>X|D[0< 'aAKyw/·{ ~$zz,'mw gZ h4M&Z4FI(9}Jwu}Y!R-w o@#T?D@l;ܸ\+ҽb`9y͗xW-aPd ä3MӢIM4M&7ʦR~UGwͺ-Lz}߱g.xgz)4yIlkn om9ZwUZ%\XuUvbUj4ESѴhZU&UnFI/pB" .A:ܜ*` Bݥp+_'UJ4GBj۩*KQ"W{e~ۂouhCP~C<.'V+'9:Is8) aNȔ.}= fl mhDwpY1%1K|A7K95nE2y@zlw͍wbX]p=ш.945S3͜DCAhvf9 w Vӊrjcz,YKM0'v~cz"h@hJ90P/= |cJ0'"w9/<0'9T!'|)-J}Ki]؃l=Ki]J䏬'|)-qXOzǓ/%'|)-hb]}5/Ki ?! aNsBœ0'9! aNsBBrIXZ`N0:٘jK PܣldJgBbi]9Y$-!/i÷b&8㹙w}JǧL M~Jǝu;%o6ٶc޷ҏs71Ȏ{Q?{&N5Μ'ÊFwHd0{ sÜk}$CڔHƨ=87;т0g_ ^xd"q0'9!I^}JocR?㠣urUԏNs 9٤~gm[Ūy_cV:]466?ֹ:Ӡ9f` džv?kd~$VUN>v|áʄ9SǬm];<M8loS!C`Il`nѻ^eTHz'Ѷ%~|?~quvҜ+?.z`Jp1)vAw5a\}Yj~#/7:Vce~u'顕Yq{>iskۂKksNe;',PsJt~2ЛG+8䡕m$:59f E')K4x{ޏJȵH ^ h;x{|cV-*Z hMXƱ'm}IǬn+4;xӼ-=~VE3ڌeG@uJ)#G[ӣصXƱ' l3cp4l7 ~id;ZFv~N'> PVJx)u֨·tJ陼tNش]{ZSgo?c?NZF 9f'LyA~N~Vjd"rϋ-y'#*Zlkzmq~ƥ:fԯYُُꡕYqUO˟e9kwѝ~tC~=~}R:feLϡm'Z\q=! aNsBœ0'9!9! aNsBR X :Is2#SK=͞˅x3zD66HNQŔT,A=.a83n;;ir{EY.q=!Q`Ld@LI43'DCAhvf9 w Vӊ2'Ćԣ|.{eqZ\-l˜aQj<'Ϟ+2$$nYߓ" p&c[ŝ@}$Z~Cœ0'9! aNaNsB~9Qx|-kj`xxU Į&Ț[mY㎢(bsE5ɖɳ&š51Gb &iCAœ.bm2=CMLrb0yZ`0Y=km5|||}Mi,kbWV˷̢}J,UE1}|ky{{ NXYQeԯ*<m25I{`Sj5V1L&oooo,kb_ykFUfX #include #include #include #include #include #include #include /* for system's IPC_xxx definitions */ #include /* for shmget, shmat, shmdt, shmctl */ #include /* for semget, semctl, semop */ #include "utils.h" #include "md5.h" void md5ify(char *inString, char *outString) { md5_state_t state; md5_byte_t digest[16]; int i; md5_init(&state); md5_append(&state, (const md5_byte_t *)inString, strlen(inString)); md5_finish(&state, digest); for (i = 0; i < 16; i++) sprintf(&outString[i * 2], "%02x", digest[i]); } void say(const char *pName, char *pMessage) { time_t the_time; struct tm *the_localtime; char timestamp[256]; the_time = time(NULL); the_localtime = localtime(&the_time); strftime(timestamp, 255, "%H:%M:%S", the_localtime); printf("%s @ %s: %s\n", pName, timestamp, pMessage); } int release_semaphore(const char *pName, int sem_id, int live_dangerously) { int rc = 0; struct sembuf op[1]; char s[1024]; say(pName, "Releasing the semaphore."); if (!live_dangerously) { op[0].sem_num = 0; op[0].sem_op = 1; op[0].sem_flg = 0; if (-1 == semop(sem_id, op, (size_t)1)) { sprintf(s, "Releasing the semaphore failed; errno is %d\n", errno); say(pName, s); } } return rc; } int acquire_semaphore(const char *pName, int sem_id, int live_dangerously) { int rc = 0; struct sembuf op[1]; char s[1024]; say(pName, "Waiting to acquire the semaphore."); if (!live_dangerously) { op[0].sem_num = 0; op[0].sem_op = -1; op[0].sem_flg = 0; if (-1 == semop(sem_id, op, (size_t)1)) { sprintf(s, "Acquiring the semaphore failed; errno is %d\n", errno); say(pName, s); } } return rc; } void read_params(struct param_struct *params) { char line[1024]; char name[1024]; int value = 0; FILE *fp; fp = fopen("params.txt", "r"); while (fgets(line, 1024, fp)) { if (strlen(line) && ('#' == line[0])) ; // comment in input, ignore else { sscanf(line, "%[ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghjiklmnopqrstuvwxyz]=%i\n", name, &value); // printf("name = %s, value = %d\n", name, value); if (!strcmp(name, "ITERATIONS")) params->iterations = value; if (!strcmp(name, "LIVE_DANGEROUSLY")) params->live_dangerously = value; if (!strcmp(name, "KEY")) params->key = value; if (!strcmp(name, "PERMISSIONS")) params->permissions = value; if (!strcmp(name, "SHM_SIZE")) params->size = value; name[0] = '\0'; value = 0; } } // printf("iterations = %d\n", params->iterations); // printf("danger = %d\n", params->live_dangerously); // printf("key = %d\n", params->key); // printf("permissions = %o\n", params->permissions); // printf("size = %d\n", params->size); } sysv_ipc-0.6.8/demo/utils.h0000644000076500000240000000050012224412321015331 0ustar mestaff00000000000000struct param_struct { int iterations; int live_dangerously; int key; int permissions; int size; }; void md5ify(char *, char *); void say(const char *, char *); int acquire_semaphore(const char *, int, int); int release_semaphore(const char *, int, int); void read_params(struct param_struct *); sysv_ipc-0.6.8/demo/utils.py0000644000076500000240000000257712224412321015552 0ustar mestaff00000000000000import time import sys PY_MAJOR_VERSION = sys.version_info[0] if PY_MAJOR_VERSION > 2: NULL_CHAR = 0 else: NULL_CHAR = '\0' def say(s): who = sys.argv[0] if who.endswith(".py"): who = who[:-3] s = "%s@%1.6f: %s" % (who, time.time(), s) print (s) def write_to_memory(memory, s): say("writing %s " % s) # Pad with NULLs in case I'm communicating with a C program. #memory.write(s + (memory.size - len(s)) * '\0') s += '\0' if PY_MAJOR_VERSION > 2: s = s.encode() memory.write(s) def read_from_memory(memory): s = memory.read() if PY_MAJOR_VERSION > 2: s = s.decode() i = s.find('\0') if i != -1: s = s[:i] say("read %s" % s) return s def read_params(): params = { } f = open("params.txt", "r") for line in f: line = line.strip() if len(line): if line.startswith('#'): pass # comment in input, ignore else: name, value = line.split('=') name = name.upper().strip() if name == "PERMISSIONS": value = int(value, 8) else: value = int(value) #print "name = %s, value = %d" % (name, value) params[name] = value f.close() return params sysv_ipc-0.6.8/demo/utils_for_2.py0000644000076500000240000000007412224412321016627 0ustar mestaff00000000000000 def raise_error(error, message): raise error, message sysv_ipc-0.6.8/demo/utils_for_3.py0000644000076500000240000000007412224412321016630 0ustar mestaff00000000000000 def raise_error(error, message): raise error(message) sysv_ipc-0.6.8/demo2/0000755000076500000240000000000012404600066014114 5ustar mestaff00000000000000sysv_ipc-0.6.8/demo2/cleanup.py0000755000076500000240000000042012224412321016107 0ustar mestaff00000000000000import sysv_ipc import utils params = utils.read_params() try: mq = sysv_ipc.MessageQueue(params["KEY"]) mq.remove() s = "message queue %d removed" % params["KEY"] print (s) except: print ("message queue doesn't exist") print ("\nAll clean!")sysv_ipc-0.6.8/demo2/conclusion.py0000644000076500000240000000331212224412321016634 0ustar mestaff00000000000000# Python modules import time import sys PY_MAJOR_VERSION = sys.version_info[0] # hashlib is only available in Python >= 2.5. I still want to support # older Pythons so I import md5 if hashlib is not available. Fortunately # md5 can masquerade as hashlib for my purposes. try: import hashlib except ImportError: import md5 as hashlib # 3rd party modules import sysv_ipc # Utils for this demo import utils if PY_MAJOR_VERSION > 2: import utils_for_3 as flex_utils else: import utils_for_2 as flex_utils params = utils.read_params() # Mrs. Premise has already created the message queue. I just need a handle # to it. mq = sysv_ipc.MessageQueue(params["KEY"]) what_i_sent = "" for i in range(0, params["ITERATIONS"]): utils.say("iteration %d" % i) s, _ = mq.receive() s = s.decode() utils.say("Received %s" % s) while s == what_i_sent: # Nothing new; give Mrs. Premise another chance to respond. mq.send(s) s, _ = mq.receive() s = s.decode() utils.say("Received %s" % s) if what_i_sent: if PY_MAJOR_VERSION > 2: what_i_sent = what_i_sent.encode() try: assert(s == hashlib.md5(what_i_sent).hexdigest()) except: flex_utils.raise_error(AssertionError, "Message corruption after %d iterations." % i) #else: # When what_i_sent is blank, this is the first message which # I always accept without question. # MD5 the reply and write back to Mrs. Premise. s = hashlib.md5(s.encode()).hexdigest() utils.say("Sending %s" % s) mq.send(s) what_i_sent = s utils.say("") utils.say("%d iterations complete" % (i + 1)) sysv_ipc-0.6.8/demo2/params.txt0000644000076500000240000000014012224412321016126 0ustar mestaff00000000000000# These parameters control how Mrs. Premise and Mrs. Conclusion behave. ITERATIONS=1000 KEY=42 sysv_ipc-0.6.8/demo2/premise.py0000644000076500000240000000353412224412321016132 0ustar mestaff00000000000000# Python modules import time import sys PY_MAJOR_VERSION = sys.version_info[0] # hashlib is only available in Python >= 2.5. I still want to support # older Pythons so I import md5 if hashlib is not available. Fortunately # md5 can masquerade as hashlib for my purposes. try: import hashlib except ImportError: import md5 as hashlib # 3rd party modules import sysv_ipc # Utils for this demo import utils if PY_MAJOR_VERSION > 2: import utils_for_3 as flex_utils else: import utils_for_2 as flex_utils utils.say("Oooo 'ello, I'm Mrs. Premise!") params = utils.read_params() # Create the message queue. mq = sysv_ipc.MessageQueue(params["KEY"], sysv_ipc.IPC_CREX) # The first message is a random string (the current time). s = time.asctime() utils.say("Sending %s" % s) mq.send(s) what_i_sent = s for i in range(0, params["ITERATIONS"]): utils.say("iteration %d" % i) s, _ = mq.receive() s = s.decode() utils.say("Received %s" % s) # If the message is what I wrote, put it back on the queue. while s == what_i_sent: # Nothing new; give Mrs. Conclusion another chance to respond. mq.send(s) s, _ = mq.receive() s = s.decode() utils.say("Received %s" % s) # What I read must be the md5 of what I wrote or something's # gone wrong. if PY_MAJOR_VERSION > 2: what_i_sent = what_i_sent.encode() try: assert(s == hashlib.md5(what_i_sent).hexdigest()) except AssertionError: flex_utils.raise_error(AssertionError, "Message corruption after %d iterations." % i) # MD5 the reply and write back to Mrs. Conclusion. s = hashlib.md5(s.encode()).hexdigest() utils.say("Sending %s" % s) mq.send(s) what_i_sent = s utils.say("") utils.say("%d iterations complete" % (i + 1)) utils.say("Destroying the message queue.") mq.remove() sysv_ipc-0.6.8/demo2/ReadMe.txt0000644000076500000240000000417112224412321016010 0ustar mestaff00000000000000This demonstrates use of message queues via two applications named after Mrs. Premise and Mrs. Conclusion of the Monty Python sketch. http://www.youtube.com/watch?v=crIJvcWkVcs Like those two characters, these programs chat back and forth and the result is a lot of nonsense. In this case, what the programs are saying isn't the interesting part. What's interesting is how they're doing it. Mrs. Premise and Mrs. Conclusion (the programs, not the sketch characters) communicate with Sys V message queues. Mrs. Premise starts things off by creating the queue and sending a random string (the current time) to it. She then sits in a loop receiving whatever message is on the queue. If it is the same message she wrote, she sends it back to the queue. If it is a new message, it must be from Mrs. Conclusion. Meanwhile, Mrs. Conclusion is doing exactly the same thing, except that she assumes Mrs. Premise will write the first message. When either of these programs receives a new message, they send back an md5 hash of that message. This serves two purposes. First, it ensures that subsequent messages are very different so that if a message somehow gets corrupted (say by being partially overwritten by the next message), it will not escape notice. Second, it ensures that corruption can be detected if it happens, because Mrs. Premise and Mrs. Conclusion can calculate what the other's response to their message should be. Since message queues manage all of the concurrency issues transparently, Mrs. Premise and Mrs. Conclusion won't ever find their messages corrupted no matter how many messages they exchange. You can experiment with this by setting ITERATIONS in params.txt to a very large value. These programs are not meant as a demonstration on how to make best use of a message queue. In fact, they're very badly behaved because they poll the queue as fast as possible -- they'll send your CPU usage right up to 100%. Remember, they're trying as hard as they can to step one another so as to expose any concurrency problems that might be present. Real code would want to sleep (or do something useful) in between calling send() and receive(). sysv_ipc-0.6.8/demo2/SampleIpcConversation.png0000644000076500000240000002720512224412321021073 0ustar mestaff00000000000000PNG  IHDR'TnsRGBbKGD̿ pHYs  tIME 2) IDATx{|Lg?gfrMl"Pմ4E.E]Zj֥4(Zjז^^EؖRU֥A5( %ȍ\&33$LJdȜgs\;!W0'9! aNsBBr͘lv8'XV!XEDI+ +UEQŠ(p kbW^*,V!3ȣWpaMk'VRۣO `šD;Z,/(&[PX[ML KYg3k'n`MĤ=u,fפDvkKyxO?~9=#I3'Vc&q:{'?r@Qr@#om^k^KgP7YwjY}51NP9v 2 3[۾nh)Vn[ܷy 5kW?+^!>čx,zDp*T}lrI 8A/<૏f݁٢˩e`#;v<'n{|K::}tju0[ 57BQR;<+G\@; ܍(>F 6lrjY}5?A5tz "w&iڭb ܏>'hS{€z8D/JpAH رe}-"p|ZdVvj3q_{'{t {7Y@;K`hW4eIWul>36۰g{pjY}ܰ4ܣ,v,ձOtscNȧ:?`C DO.o?[[}2F"ma,{ԲPC73[ɖKyauv}/֬pjĆZ>F|D}lgsp2m$]Rj9˩euT3Onuk[BC r50P/ZrSÜ0'9! aNsBBrIXZ`N039)f=FtI~)t/.ڴO85 ^"iyNx6p~_3'<7sNjn׽ǧL ogYsn:׮ہm(y)6"ۜc޷ҏs71Ȏ{lQ?{&N5Μ'ÊJQfpevrn~m_)6 iS4>f6Fuq'j{RP挐 |+QCߣvt+6"q0'9!5rsW$.:hÍ9S(F6ZY.yND>Fsm~/i=t{?Q u8:uRo7"G[ӑOwH0Oim&E*ۜkT@L}b3|BKiaN֑ \n(w+sy$O<[FmPqCw!֍Crxj!8A%xiplu$|aE5%Z;yOU_iz<Q:8u oO4/B {zj AM3'2[g%3,{`󓳀S7MvWU> ?DO^~>?۵;n='!~;^^[gg h9ݻn+Ų{oA4=M+7V$oN 7Mhxlq{~$K@r/թ3srg×>? ,a,h5-ݱd"{ N{||zoz%@ݼMmFg‚aK̉b~ xtu@`؂ӇoUH,'j?E;'_Ydm`NR(#6 5؈ޟܧ%֘%\OܒÁYZ:8kjZJQϣ%땦MO_+V6m v0pp9Ft.z&%m^_:$2!yFNNOT˔|i jUp&tVpŠdS(QX;^a Vjx߾4IY_1A\+|u+tSo]+7odJty͞˅x3zD66[đ<{ W/;SS|HۺV:?q#gL^vhs4=֢,xynT(1MZq0%<̜ƣ٧D߁t[O+˜cz,YKMgNݜ~Mk}K1=4 [-oW`a(Pm.FozBwk@Edێi{J?'71Ȏ{Q?{&N5\Oj~R$mg=Q iS"mgaz@w jÜ3k&Ez@>nF}VG~5OК2qO :mX'?iQ5'~Xtƴړ %uK} kV~f-,l5NWO3ڌ]J7lUFɽ/nMhmTO}٦h6I#B3`דkDm?w&6ul { ӁW <띛>6(`ZeSnAk*ּ!t7O?GJ7l@c1|WO-MxdM#;_ZڔaOjGjdvĦSX+U}b?䩟:Z/{lM5 +P]JQǠuT\ Ӱˏa*Q_Yboy}S8nsҿs5|7[D*k`8 ϯT0'9! aNsBœœ0'9! aNœ\90'UfdJt)G޷soF҆6]aN${ WbJb* yow[rj܊~;;ir{EY.qjhDirT1MZq0%<̩aN4fgo ~an?h.9!#, ^Yb ;ϩaNv/娿&ĵ߾jf"籒bNf!0-;8{Of`;83̉?`yq0'9!OXGJ.qgzd |&VRe)S&oM=ÎlNmJP4фɭZ{*7#nt֏"]f}$sr tB`0 O5+VݼM6߾aͺ5 ЭEdz=l Vr"|02;gZAQ.׋9x @u"￰ S:iB_,Hy0};{cklVP?]T,фǺ}u"H)egp5_/~tJ6R /{S kݩ~:n6J Y]T,c㧑O zlֻt1MlP;4GJ.E=b-w}^,`Xz}5hUclw͍wbX]p=шҌ>c&_`Jz=x&]̉Ӄ\s"QA@:̭0'61X|s+sRla~95̉1ׄ۷OmABLj裾y }̉+G͜}̉KW>jeq0'9!>j"&ޡxܡ+9+9z>jb}z%.飾^œ0'9! aNsBsBœ0'9! aNaNsBbGK3'9 qS?sfęq3SpO?sn9q?QϜkP7-cqs3kl`{k|Zޮ.Qg]:׮ہm(y~\O nb@i%xޅv3!Z,]H0{ #ү1ҦD6FuQcJ}IAm3BsfppՄ'nNmYq0'9!̉ۢ׬䯪9x*bE쩲kEMT Ne4.;tZv=0'7^nD&BṪW2T1#R&UnMԇ-o+ZѫSft]])B~䇟U:;66~v.œ#7@x0ذHRkE5p! !B0WKbjX3ҝVp㋋?Jqn$6C3wE^MK13!sBLI!M㻅pTi0!rqӓ~?5zR{ @ӃEh VΫ0nwМiR٤MѥkȿoM).Y= @m+ʑ^?wN8`G/=]6tq=[!>yE!Yn8"OzoNyѷrBQkBtJqB\.%4 IDATz ͉DBNF/-]>]yGŒĺ!7- !^|T!m͘G!ıNB!Dm_;"-/ཙ\O{ p ^uS]NќiR!h.]6FIr/)ut]7{# {!G)_b6&Rpm qnc]n^&6J.@##_l7p`O~x899%yc| k֭͛S(it-,A^>X|D[0< 'aAKyw/·{ ~$zz,'mw gZ h4M&Z4FI(9}Jwu}Y!R-w o@#T?D@l;ܸ\+ҽb`9y͗xW-aPd ä3MӢIM4M&7ʦR~UGwͺ-Lz}߱g.xgz)4yIlkn om9ZwUZ%\XuUvbUj4ESѴhZU&UnFI/pB" .A:ܜ*` Bݥp+_'UJ4GBj۩*KQ"W{e~ۂouhCP~C<.'V+'9:Is8) aNȔ.}= fl mhDwpY1%1K|A7K95nE2y@zlw͍wbX]p=ш.945S3͜DCAhvf9 w Vӊrjcz,YKM0'v~cz"h@hJ90P/= |cJ0'"w9/<0'9T!'|)-J}Ki]؃l=Ki]J䏬'|)-qXOzǓ/%'|)-hb]}5/Ki ?! aNsBœ0'9! aNsBBrIXZ`N0:٘jK PܣldJgBbi]9Y$-!/i÷b&8㹙w}JǧL M~Jǝu;%o6ٶc޷ҏs71Ȏ{Q?{&N5Μ'ÊFwHd0{ sÜk}$CڔHƨ=87;т0g_ ^xd"q0'9!I^}JocR?㠣urUԏNs 9٤~gm[Ūy_cV:]466?ֹ:Ӡ9f` džv?kd~$VUN>v|áʄ9SǬm];<M8loS!C`Il`nѻ^eTHz'Ѷ%~|?~quvҜ+?.z`Jp1)vAw5a\}Yj~#/7:Vce~u'顕Yq{>iskۂKksNe;',PsJt~2ЛG+8䡕m$:59f E')K4x{ޏJȵH ^ h;x{|cV-*Z hMXƱ'm}IǬn+4;xӼ-=~VE3ڌeG@uJ)#G[ӣصXƱ' l3cp4l7 ~id;ZFv~N'> PVJx)u֨·tJ陼tNش]{ZSgo?c?NZF 9f'LyA~N~Vjd"rϋ-y'#*Zlkzmq~ƥ:fԯYُُꡕYqUO˟e9kwѝ~tC~=~}R:feLϡm'Z\q=! aNsBœ0'9!9! aNsBR X :Is2#SK=͞˅x3zD66HNQŔT,A=.a83n;;ir{EY.q=!Q`Ld@LI43'DCAhvf9 w Vӊ2'Ćԣ|.{eqZ\-l˜aQj<'Ϟ+2$$nYߓ" p&c[ŝ@}$Z~Cœ0'9! aNaNsB~9Qx|-kj`xxU Į&Ț[mY㎢(bsE5ɖɳ&š51Gb &iCAœ.bm2=CMLrb0yZ`0Y=km5|||}Mi,kbWV˷̢}J,UE1}|ky{{ NXYQeԯ*<m25I{`Sj5V1L&oooo,kb_ykFUfX 1: ndups += len(d[key]) print key, d[key] print "Out of {0} unique filenames, I found {1} duplicate keys.".format(nfilenames, ndups) sysv_ipc-0.6.8/INSTALL0000644000076500000240000000012112224412321014124 0ustar mestaff00000000000000To install, just use the normal setup.py routine: sudo python setup.py install sysv_ipc-0.6.8/LICENSE0000644000076500000240000000267712224412321014122 0ustar mestaff00000000000000Copyright (c) 2008, Philip Semanchuk All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of sysv_ipc nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY Philip Semanchuk ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Philip Semanchuk BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. sysv_ipc-0.6.8/memory.c0000644000076500000240000005433112224412321014563 0ustar mestaff00000000000000#include "Python.h" #include "structmember.h" #include "common.h" #include "memory.h" /****************** Internal use only **********************/ PyObject * shm_str(SharedMemory *self) { #if PY_MAJOR_VERSION > 2 return PyUnicode_FromFormat("Key=%ld, id=%d", (long)self->key, self->id); #else return PyString_FromFormat("Key=%ld, id=%d", (long)self->key, self->id); #endif } PyObject * shm_repr(SharedMemory *self) { #if PY_MAJOR_VERSION > 2 return PyUnicode_FromFormat("sysv_ipc.SharedMemory(%ld)", (long)self->key); #else return PyString_FromFormat("sysv_ipc.SharedMemory(%ld)", (long)self->key); #endif } PyObject * shm_attach(SharedMemory *self, int shmat_flags) { DPRINTF("attaching memory @ address %p with id %d using flags 0x%x\n", self->address, self->id, shmat_flags); self->address = shmat(self->id, self->address, shmat_flags); if ( (void *)-1 == self->address) { self->address = NULL; switch (errno) { case EACCES: PyErr_SetString(pPermissionsException, "No permission to attach"); break; case ENOMEM: PyErr_SetString(PyExc_MemoryError, "Not enough memory"); break; case EINVAL: PyErr_SetString(PyExc_ValueError, "Invalid id or address"); break; default: PyErr_SetFromErrno(PyExc_OSError); break; } goto error_return; } Py_RETURN_NONE; error_return: return NULL; } PyObject * shm_remove(int shared_memory_id) { struct shmid_ds shm_info; DPRINTF("removing shm with id %d\n", shared_memory_id); if (-1 == shmctl(shared_memory_id, IPC_RMID, &shm_info)) { switch (errno) { case EIDRM: case EINVAL: PyErr_Format(pExistentialException, "No shared memory with id %d exists", shared_memory_id); break; case EPERM: PyErr_SetString(pPermissionsException, "You do not have permission to remove the shared memory"); break; default: PyErr_SetFromErrno(PyExc_OSError); break; } goto error_return; } Py_RETURN_NONE; error_return: return NULL; } static PyObject * shm_get_value(int shared_memory_id, enum GET_SET_IDENTIFIERS field) { struct shmid_ds shm_info; PyObject *py_value = NULL; DPRINTF("Calling shmctl(...IPC_STAT...), field = %d\n", field); if (-1 == shmctl(shared_memory_id, IPC_STAT, &shm_info)) { switch (errno) { case EIDRM: case EINVAL: PyErr_Format(pExistentialException, "No shared memory with id %d exists", shared_memory_id); break; case EACCES: PyErr_SetString(pPermissionsException, "You do not have permission to read the shared memory attribute"); break; default: PyErr_SetFromErrno(PyExc_OSError); break; } goto error_return; } switch (field) { case SVIFP_SHM_SIZE: py_value = SIZE_T_TO_PY(shm_info.shm_segsz); break; case SVIFP_SHM_LAST_ATTACH_TIME: py_value = TIME_T_TO_PY(shm_info.shm_atime); break; case SVIFP_SHM_LAST_DETACH_TIME: py_value = TIME_T_TO_PY(shm_info.shm_dtime); break; case SVIFP_SHM_LAST_CHANGE_TIME: py_value = TIME_T_TO_PY(shm_info.shm_ctime); break; case SVIFP_SHM_CREATOR_PID: py_value = PID_T_TO_PY(shm_info.shm_cpid); break; case SVIFP_SHM_LAST_AT_DT_PID: py_value = PID_T_TO_PY(shm_info.shm_lpid); break; case SVIFP_SHM_NUMBER_ATTACHED: // shm_nattch is unsigned // ref: http://www.opengroup.org/onlinepubs/007908799/xsh/sysshm.h.html #if PY_MAJOR_VERSION > 2 py_value = PyLong_FromUnsignedLong(shm_info.shm_nattch); #else py_value = py_int_or_long_from_ulong(shm_info.shm_nattch); #endif break; case SVIFP_IPC_PERM_UID: py_value = UID_T_TO_PY(shm_info.shm_perm.uid); break; case SVIFP_IPC_PERM_GID: py_value = GID_T_TO_PY(shm_info.shm_perm.gid); break; case SVIFP_IPC_PERM_CUID: py_value = UID_T_TO_PY(shm_info.shm_perm.cuid); break; case SVIFP_IPC_PERM_CGID: py_value = GID_T_TO_PY(shm_info.shm_perm.cgid); break; case SVIFP_IPC_PERM_MODE: py_value = MODE_T_TO_PY(shm_info.shm_perm.mode); break; default: PyErr_Format(pInternalException, "Bad field %d passed to shm_get_value", field); goto error_return; break; } return py_value; error_return: return NULL; } static int shm_set_ipc_perm_value(int id, enum GET_SET_IDENTIFIERS field, union ipc_perm_value value) { struct shmid_ds shm_info; if (-1 == shmctl(id, IPC_STAT, &shm_info)) { switch (errno) { case EIDRM: case EINVAL: PyErr_Format(pExistentialException, "No shared memory with id %d exists", id); break; case EACCES: PyErr_SetString(pPermissionsException, "You do not have permission to read the shared memory attribute"); break; default: PyErr_SetFromErrno(PyExc_OSError); break; } goto error_return; } switch (field) { case SVIFP_IPC_PERM_UID: shm_info.shm_perm.uid = value.uid; break; case SVIFP_IPC_PERM_GID: shm_info.shm_perm.gid = value.gid; break; case SVIFP_IPC_PERM_MODE: shm_info.shm_perm.mode = value.mode; break; default: PyErr_Format(pInternalException, "Bad field %d passed to shm_set_ipc_perm_value", field); goto error_return; break; } if (-1 == shmctl(id, IPC_SET, &shm_info)) { switch (errno) { case EIDRM: case EINVAL: PyErr_Format(pExistentialException, "No shared memory with id %d exists", id); break; case EPERM: PyErr_SetString(pPermissionsException, "You do not have permission to change the shared memory's attributes"); break; default: PyErr_SetFromErrno(PyExc_OSError); break; } goto error_return; } return 0; error_return: return -1; } /****************** Class methods **********************/ void SharedMemory_dealloc(SharedMemory *self) { Py_TYPE(self)->tp_free((PyObject*)self); } PyObject * SharedMemory_new(PyTypeObject *type, PyObject *args, PyObject *kwlist) { SharedMemory *self; self = (SharedMemory *)type->tp_alloc(type, 0); if (NULL != self) { self->key = (key_t)-1; self->id = 0; self->address = NULL; } return (PyObject *)self; } int SharedMemory_init(SharedMemory *self, PyObject *args, PyObject *keywords) { NoneableKey key; int mode = 0600; unsigned long size = 0; int shmget_flags = 0; int shmat_flags = 0; char init_character = ' '; char *keyword_list[ ] = {"key", "flags", "mode", "size", "init_character", NULL}; PyObject *py_size = NULL; DPRINTF("Inside SharedMemory_init()\n"); if (!PyArg_ParseTupleAndKeywords(args, keywords, "O&|iikc", keyword_list, &convert_key_param, &key, &shmget_flags, &mode, &size, &init_character)) goto error_return; mode &= 0777; shmget_flags &= ~0777; DPRINTF("key is none = %d, key value = %ld\n", key.is_none, (long)key.value); if ( !(shmget_flags & IPC_CREAT) && (shmget_flags & IPC_EXCL) ) { PyErr_SetString(PyExc_ValueError, "IPC_EXCL must be combined with IPC_CREAT"); goto error_return; } if (key.is_none && ((shmget_flags & IPC_EXCL) != IPC_EXCL)) { PyErr_SetString(PyExc_ValueError, "Key can only be None if IPC_EXCL is set"); goto error_return; } // When creating a new segment, the default size is PAGE_SIZE. if (((shmget_flags & IPC_CREX) == IPC_CREX) && (!size)) size = PAGE_SIZE; if (key.is_none) { // (key == None) ==> generate a key for the caller do { errno = 0; self->key = get_random_key(); DPRINTF("Calling shmget, key=%ld, size=%lu, mode=%o, flags=0x%x\n", (long)self->key, size, mode, shmget_flags); self->id = shmget(self->key, size, mode | shmget_flags); } while ( (-1 == self->id) && (EEXIST == errno) ); } else { // (key != None) ==> use key supplied by the caller self->key = key.value; DPRINTF("Calling shmget, key=%ld, size=%lu, mode=%o, flags=0x%x\n", (long)self->key, size, mode, shmget_flags); self->id = shmget(self->key, size, mode | shmget_flags); } DPRINTF("id == %d\n", self->id); if (self->id == -1) { switch (errno) { case EACCES: PyErr_Format(pPermissionsException, "Permission %o cannot be granted on the existing segment", mode); break; case EEXIST: PyErr_Format(pExistentialException, "Shared memory with the key %ld already exists", (long)self->key); break; case ENOENT: PyErr_Format(pExistentialException, "No shared memory exists with the key %ld", (long)self->key); break; case EINVAL: PyErr_SetString(PyExc_ValueError, "The size is invalid"); break; case ENOMEM: PyErr_SetString(PyExc_MemoryError, "Not enough memory"); break; case ENOSPC: PyErr_SetString(PyExc_OSError, "Not enough shared memory identifiers available (ENOSPC)"); break; default: PyErr_SetFromErrno(PyExc_OSError); break; } goto error_return; } // Attach the memory. If no write permissions requested, attach read-only. shmat_flags = (mode & 0200) ? 0 : SHM_RDONLY; if (NULL == shm_attach(self, shmat_flags)) { // Bad news, something went wrong. goto error_return; } if ( ((shmget_flags & IPC_CREX) == IPC_CREX) && (!(shmat_flags & SHM_RDONLY)) ) { // Initialize the memory. py_size = shm_get_value(self->id, SVIFP_SHM_SIZE); if (!py_size) goto error_return; else { #if PY_MAJOR_VERSION > 2 size = PyLong_AsUnsignedLongMask(py_size); #else size = PyInt_AsUnsignedLongMask(py_size); #endif DPRINTF("memsetting address %p to %lu bytes of ASCII 0x%x (%c)\n", \ self->address, size, (int)init_character, init_character); memset(self->address, init_character, size); } Py_DECREF(py_size); } return 0; error_return: return -1; } PyObject * SharedMemory_attach(SharedMemory *self, PyObject *args) { PyObject *py_address = NULL; void *address = NULL; int flags = 0; if (!PyArg_ParseTuple(args, "|Oi", &py_address, &flags)) goto error_return; if ((!py_address) || (py_address == Py_None)) address = NULL; else { if (PyLong_Check(py_address)) address = PyLong_AsVoidPtr(py_address); else { PyErr_SetString(PyExc_TypeError, "address must be a long"); goto error_return; } } self->address = shmat(self->id, address, flags); if ((void *)-1 == self->address) { self->address = NULL; switch (errno) { case EACCES: PyErr_SetString(pPermissionsException, "No permission to attach"); break; case EINVAL: PyErr_SetString(PyExc_ValueError, "Invalid address or flags"); break; case ENOMEM: PyErr_SetString(PyExc_MemoryError, "Not enough memory"); break; default: PyErr_SetFromErrno(PyExc_OSError); break; } goto error_return; } Py_RETURN_NONE; error_return: return NULL; } PyObject * SharedMemory_detach(SharedMemory *self) { if (-1 == shmdt(self->address)) { self->address = NULL; switch (errno) { case EINVAL: PyErr_SetNone(pNotAttachedException); break; default: PyErr_SetFromErrno(PyExc_OSError); break; } goto error_return; } self->address = NULL; Py_RETURN_NONE; error_return: return NULL; } PyObject * SharedMemory_read(SharedMemory *self, PyObject *args, PyObject *keywords) { /* Tricky business here. A memory segment's size is a size_t which is ulong or smaller. However, the largest string that Python can construct is of ssize_t which is long or smaller. Therefore, the size and offset variables must be ulongs while the byte_count must be a long (and must not exceed LONG_MAX). Mind your math! */ long byte_count = 0; unsigned long offset = 0; unsigned long size; PyObject *py_size; char *keyword_list[ ] = {"byte_count", "offset", NULL}; if (!PyArg_ParseTupleAndKeywords(args, keywords, "|lk", keyword_list, &byte_count, &offset)) goto error_return; if (self->address == NULL) { PyErr_SetString(pNotAttachedException, "Read attempt on unattached memory segment"); goto error_return; } if ( (py_size = shm_get_value(self->id, SVIFP_SHM_SIZE)) ) { #if PY_MAJOR_VERSION > 2 size = PyLong_AsUnsignedLongMask(py_size); #else size = PyInt_AsUnsignedLongMask(py_size); #endif Py_DECREF(py_size); } else goto error_return; DPRINTF("offset = %lu, byte_count = %ld, size = %lu\n", offset, byte_count, size); if (offset >= size) { PyErr_SetString(PyExc_ValueError, "The offset must be less than the segment size"); goto error_return; } if (byte_count < 0) { PyErr_SetString(PyExc_ValueError, "The byte_count cannot be negative"); goto error_return; } /* If the caller didn't specify a byte count or specified one that would read past the end of the segment, return everything from the offset to the end of the segment. Be careful here not to express the second if condition w/addition, e.g. (byte_count + offset > size) It might be more intuitive but since byte_count is a long and offset is a ulong, their sum could cause an arithmetic overflow. */ if ((!byte_count) || ((unsigned long)byte_count > size - offset)) { // byte_count needs to be calculated if (size - offset <= (unsigned long)PY_STRING_LENGTH_MAX) byte_count = size - offset; else { // Caller is asking for more bytes than I can stuff into // a Python string. PyErr_Format(PyExc_ValueError, "The byte_count cannot exceed Python's max string length %ld", (long)PY_STRING_LENGTH_MAX); goto error_return; } } #if PY_MAJOR_VERSION > 2 return PyBytes_FromStringAndSize(self->address + offset, byte_count); #else return PyString_FromStringAndSize(self->address + offset, byte_count); #endif error_return: return NULL; } PyObject * SharedMemory_write(SharedMemory *self, PyObject *args, PyObject *kw) { /* See comments for read() regarding "size issues". Note that here Python provides the byte_count so it can't be negative. In Python >= 2.5, the Python argument specifier 's#' expects a py_ssize_t for its second parameter. A long is long enough. It might be too big, though, on platforms where a long is larger than py_ssize_t. Therefore I *must* initialize it to 0 so that whatever Python doesn't write to is zeroed out. */ unsigned long offset = 0; unsigned long size; PyObject *py_size; char *keyword_list[ ] = {"s", "offset", NULL}; #if PY_MAJOR_VERSION > 2 static char args_format[] = "s*|l"; Py_buffer data; #else static char args_format[] = "s#|l"; typedef struct { const char *buf; long len; } MyBuffer; MyBuffer data; data.len = 0; #endif if (!PyArg_ParseTupleAndKeywords(args, kw, args_format, keyword_list, #if PY_MAJOR_VERSION > 2 &data, #else &(data.buf), &(data.len), #endif &offset)) goto error_return; if (self->address == NULL) { PyErr_SetString(pNotAttachedException, "Write attempt on unattached memory segment"); goto error_return; } if ( (py_size = shm_get_value(self->id, SVIFP_SHM_SIZE)) ) { #if PY_MAJOR_VERSION > 2 size = PyLong_AsUnsignedLongMask(py_size); #else size = PyInt_AsUnsignedLongMask(py_size); #endif Py_DECREF(py_size); } else goto error_return; DPRINTF("write size check; size=%lu, offset=%lu, dat.len=%ld\n", size, offset, data.len); if ((unsigned long)data.len > size - offset) { PyErr_SetString(PyExc_ValueError, "Attempt to write past end of memory segment"); goto error_return; } memcpy((self->address + offset), data.buf, data.len); Py_RETURN_NONE; error_return: return NULL; } PyObject * SharedMemory_remove(SharedMemory *self) { return shm_remove(self->id); } PyObject * shm_get_key(SharedMemory *self) { return KEY_T_TO_PY(self->key); } PyObject * shm_get_size(SharedMemory *self) { return shm_get_value(self->id, SVIFP_SHM_SIZE); } PyObject * shm_get_address(SharedMemory *self) { return PyLong_FromVoidPtr(self->address); } PyObject * shm_get_attached(SharedMemory *self) { if (self->address) Py_RETURN_TRUE; else Py_RETURN_FALSE; } PyObject * shm_get_last_attach_time(SharedMemory *self) { return shm_get_value(self->id, SVIFP_SHM_LAST_ATTACH_TIME); } PyObject * shm_get_last_detach_time(SharedMemory *self) { return shm_get_value(self->id, SVIFP_SHM_LAST_DETACH_TIME); } PyObject * shm_get_last_change_time(SharedMemory *self) { return shm_get_value(self->id, SVIFP_SHM_LAST_CHANGE_TIME); } PyObject * shm_get_creator_pid(SharedMemory *self) { return shm_get_value(self->id, SVIFP_SHM_CREATOR_PID); } PyObject * shm_get_last_pid(SharedMemory *self) { return shm_get_value(self->id, SVIFP_SHM_LAST_AT_DT_PID); } PyObject * shm_get_number_attached(SharedMemory *self) { return shm_get_value(self->id, SVIFP_SHM_NUMBER_ATTACHED); } PyObject * shm_get_uid(SharedMemory *self) { return shm_get_value(self->id, SVIFP_IPC_PERM_UID); } PyObject * shm_get_cuid(SharedMemory *self) { return shm_get_value(self->id, SVIFP_IPC_PERM_CUID); } PyObject * shm_get_cgid(SharedMemory *self) { return shm_get_value(self->id, SVIFP_IPC_PERM_CGID); } PyObject * shm_get_mode(SharedMemory *self) { return shm_get_value(self->id, SVIFP_IPC_PERM_MODE); } int shm_set_uid(SharedMemory *self, PyObject *py_value) { union ipc_perm_value new_value; #if PY_MAJOR_VERSION > 2 if (!PyLong_Check(py_value)) #else if (!PyInt_Check(py_value)) #endif { PyErr_SetString(PyExc_TypeError, "Attribute 'uid' must be an integer"); goto error_return; } #if PY_MAJOR_VERSION > 2 new_value.uid = PyLong_AsLong(py_value); #else new_value.uid = PyInt_AsLong(py_value); #endif if (((uid_t)-1 == new_value.uid) && PyErr_Occurred()) { // no idea what could have gone wrong -- punt it up to the caller goto error_return; } return shm_set_ipc_perm_value(self->id, SVIFP_IPC_PERM_UID, new_value); error_return: return -1; } PyObject * shm_get_gid(SharedMemory *self) { return shm_get_value(self->id, SVIFP_IPC_PERM_GID); } int shm_set_gid(SharedMemory *self, PyObject *py_value) { union ipc_perm_value new_value; #if PY_MAJOR_VERSION > 2 if (!PyLong_Check(py_value)) #else if (!PyInt_Check(py_value)) #endif { PyErr_Format(PyExc_TypeError, "attribute 'gid' must be an integer"); goto error_return; } #if PY_MAJOR_VERSION > 2 new_value.gid = PyLong_AsLong(py_value); #else new_value.gid = PyInt_AsLong(py_value); #endif if (((gid_t)-1 == new_value.gid) && PyErr_Occurred()) { // no idea what could have gone wrong -- punt it up to the caller goto error_return; } return shm_set_ipc_perm_value(self->id, SVIFP_IPC_PERM_GID, new_value); error_return: return -1; } int shm_set_mode(SharedMemory *self, PyObject *py_value) { union ipc_perm_value new_value; #if PY_MAJOR_VERSION > 2 if (!PyLong_Check(py_value)) #else if (!PyInt_Check(py_value)) #endif { PyErr_Format(PyExc_TypeError, "attribute 'mode' must be an integer"); goto error_return; } #if PY_MAJOR_VERSION > 2 new_value.mode = PyLong_AsLong(py_value); #else new_value.mode = PyInt_AsLong(py_value); #endif if (((mode_t)-1 == new_value.mode) && PyErr_Occurred()) { // no idea what could have gone wrong -- punt it up to the caller goto error_return; } return shm_set_ipc_perm_value(self->id, SVIFP_IPC_PERM_MODE, new_value); error_return: return -1; } sysv_ipc-0.6.8/memory.h0000644000076500000240000000331412224412321014563 0ustar mestaff00000000000000typedef struct { PyObject_HEAD key_t key; int id; void *address; } SharedMemory; /* Union for passing values to shm_set_ipc_perm_value() */ union ipc_perm_value { uid_t uid; gid_t gid; mode_t mode; }; /* Object methods */ PyObject *SharedMemory_new(PyTypeObject *, PyObject *, PyObject *); int SharedMemory_init(SharedMemory *, PyObject *, PyObject *); void SharedMemory_dealloc(SharedMemory *); PyObject *SharedMemory_attach(SharedMemory *, PyObject *); PyObject *SharedMemory_detach(SharedMemory *); PyObject *SharedMemory_read(SharedMemory *, PyObject *, PyObject *); PyObject *SharedMemory_write(SharedMemory *, PyObject *, PyObject *); PyObject *SharedMemory_remove(SharedMemory *); /* Object attributes (read-write & read-only) */ PyObject *shm_get_uid(SharedMemory *); int shm_set_uid(SharedMemory *, PyObject *); PyObject *shm_get_gid(SharedMemory *); int shm_set_gid(SharedMemory *, PyObject *); PyObject *shm_get_mode(SharedMemory *); int shm_set_mode(SharedMemory *, PyObject *); PyObject *shm_get_key(SharedMemory *); PyObject *shm_get_size(SharedMemory *); PyObject *shm_get_address(SharedMemory *); PyObject *shm_get_attached(SharedMemory *); PyObject *shm_get_last_attach_time(SharedMemory *); PyObject *shm_get_last_detach_time(SharedMemory *); PyObject *shm_get_last_change_time(SharedMemory *); PyObject *shm_get_creator_pid(SharedMemory *); PyObject *shm_get_last_pid(SharedMemory *); PyObject *shm_get_number_attached(SharedMemory *); PyObject *shm_get_cuid(SharedMemory *); PyObject *shm_get_cgid(SharedMemory *); PyObject *shm_str(SharedMemory *); PyObject *shm_repr(SharedMemory *); /* Utility functions */ PyObject *shm_remove(int); PyObject *shm_attach(SharedMemory *, int); sysv_ipc-0.6.8/mq.c0000644000076500000240000004417212224412321013672 0ustar mestaff00000000000000#include "Python.h" #include "structmember.h" #include "common.h" #include "mq.h" PyObject * mq_str(MessageQueue *self) { #if PY_MAJOR_VERSION > 2 return PyUnicode_FromFormat("Key=%ld, id=%d", (long)self->key, self->id); #else return PyString_FromFormat("Key=%ld, id=%d", (long)self->key, self->id); #endif } PyObject * mq_repr(MessageQueue *self) { #if PY_MAJOR_VERSION > 2 return PyUnicode_FromFormat("sysv_ipc.MessageQueue(%ld)", (long)self->key); #else return PyString_FromFormat("sysv_ipc.MessageQueue(%ld)", (long)self->key); #endif } static PyObject * get_a_value(int queue_id, enum GET_SET_IDENTIFIERS field) { struct msqid_ds q_info; PyObject *py_value = NULL; DPRINTF("Calling msgctl(...IPC_STAT...), field = %d\n", field); if (-1 == msgctl(queue_id, IPC_STAT, &q_info)) { switch (errno) { case EIDRM: case EINVAL: PyErr_Format(pExistentialException, "The queue no longer exists"); break; case EACCES: PyErr_SetString(pPermissionsException, "Permission denied"); break; default: PyErr_SetFromErrno(PyExc_OSError); break; } goto error_return; } switch (field) { case SVIFP_MQ_LAST_SEND_TIME: py_value = TIME_T_TO_PY(q_info.msg_stime); break; case SVIFP_MQ_LAST_RECEIVE_TIME: py_value = TIME_T_TO_PY(q_info.msg_rtime); break; case SVIFP_MQ_LAST_CHANGE_TIME: py_value = TIME_T_TO_PY(q_info.msg_ctime); break; case SVIFP_MQ_CURRENT_MESSAGES: py_value = MSGQNUM_T_TO_PY(q_info.msg_qnum); break; case SVIFP_MQ_QUEUE_BYTES_MAX: py_value = MSGLEN_T_TO_PY(q_info.msg_qbytes); break; case SVIFP_MQ_LAST_SEND_PID: py_value = PID_T_TO_PY(q_info.msg_lspid); break; case SVIFP_MQ_LAST_RECEIVE_PID: py_value = PID_T_TO_PY(q_info.msg_lrpid); break; case SVIFP_IPC_PERM_UID: py_value = UID_T_TO_PY(q_info.msg_perm.uid); break; case SVIFP_IPC_PERM_GID: py_value = GID_T_TO_PY(q_info.msg_perm.gid); break; case SVIFP_IPC_PERM_CUID: py_value = UID_T_TO_PY(q_info.msg_perm.cuid); break; case SVIFP_IPC_PERM_CGID: py_value = GID_T_TO_PY(q_info.msg_perm.cgid); break; case SVIFP_IPC_PERM_MODE: py_value = MODE_T_TO_PY(q_info.msg_perm.mode); break; default: PyErr_Format(pInternalException, "Bad field %d passed to get_a_value", field); goto error_return; break; } return py_value; error_return: return NULL; } int set_a_value(int id, enum GET_SET_IDENTIFIERS field, PyObject *py_value) { struct msqid_ds mq_info; #if PY_MAJOR_VERSION > 2 if (!PyLong_Check(py_value)) #else if (!PyInt_Check(py_value)) #endif { PyErr_Format(PyExc_TypeError, "The attribute must be an integer"); goto error_return; } /* Here I get the current values associated with the queue. It's critical to populate sem_info with current values here (rather than just using the struct filled with whatever garbage it acquired from being declared on the stack) because the call to msgctl(...IPC_SET...) below will copy uid, gid and mode to the kernel's data structure. */ if (-1 == msgctl(id, IPC_STAT, &mq_info)) { switch (errno) { case EACCES: case EPERM: PyErr_SetString(pPermissionsException, "Permission denied"); break; case EINVAL: PyErr_SetString(pExistentialException, "The queue no longer exists"); break; default: PyErr_SetFromErrno(PyExc_OSError); break; } goto error_return; } switch (field) { case SVIFP_IPC_PERM_UID: #if PY_MAJOR_VERSION > 2 mq_info.msg_perm.uid = PyLong_AsLong(py_value); #else mq_info.msg_perm.uid = PyInt_AsLong(py_value); #endif break; case SVIFP_IPC_PERM_GID: #if PY_MAJOR_VERSION > 2 mq_info.msg_perm.gid = PyLong_AsLong(py_value); #else mq_info.msg_perm.gid = PyInt_AsLong(py_value); #endif break; case SVIFP_IPC_PERM_MODE: #if PY_MAJOR_VERSION > 2 mq_info.msg_perm.mode = PyLong_AsLong(py_value); #else mq_info.msg_perm.mode = PyInt_AsLong(py_value); #endif break; case SVIFP_MQ_QUEUE_BYTES_MAX: // A msglen_t is unsigned. // ref: http://www.opengroup.org/onlinepubs/000095399/basedefs/sys/msg.h.html #if PY_MAJOR_VERSION > 2 mq_info.msg_qbytes = PyLong_AsUnsignedLongMask(py_value); #else mq_info.msg_qbytes = PyInt_AsUnsignedLongMask(py_value); #endif break; default: PyErr_Format(pInternalException, "Bad field %d passed to set_a_value", field); goto error_return; break; } if (-1 == msgctl(id, IPC_SET, &mq_info)) { switch (errno) { case EACCES: case EPERM: PyErr_SetString(pPermissionsException, "Permission denied"); break; case EINVAL: PyErr_SetString(pExistentialException, "The queue no longer exists"); break; default: PyErr_SetFromErrno(PyExc_OSError); break; } goto error_return; } return 0; error_return: return -1; } PyObject * mq_get_key(MessageQueue *self) { return KEY_T_TO_PY(self->key); } PyObject * mq_get_last_send_time(MessageQueue *self) { return get_a_value(self->id, SVIFP_MQ_LAST_SEND_TIME); } PyObject * mq_get_last_receive_time(MessageQueue *self) { return get_a_value(self->id, SVIFP_MQ_LAST_RECEIVE_TIME); } PyObject * mq_get_last_change_time(MessageQueue *self) { return get_a_value(self->id, SVIFP_MQ_LAST_CHANGE_TIME); } PyObject * mq_get_last_send_pid(MessageQueue *self) { return get_a_value(self->id, SVIFP_MQ_LAST_SEND_PID); } PyObject * mq_get_last_receive_pid(MessageQueue *self) { return get_a_value(self->id, SVIFP_MQ_LAST_RECEIVE_PID); } PyObject * mq_get_current_messages(MessageQueue *self) { return get_a_value(self->id, SVIFP_MQ_CURRENT_MESSAGES); } PyObject * mq_get_max_size(MessageQueue *self) { return get_a_value(self->id, SVIFP_MQ_QUEUE_BYTES_MAX); } int mq_set_max_size(MessageQueue *self, PyObject *py_value) { return set_a_value(self->id, SVIFP_MQ_QUEUE_BYTES_MAX, py_value); } PyObject * mq_get_mode(MessageQueue *self) { return get_a_value(self->id, SVIFP_IPC_PERM_MODE); } int mq_set_mode(MessageQueue *self, PyObject *py_value) { return set_a_value(self->id, SVIFP_IPC_PERM_MODE, py_value); } PyObject * mq_get_uid(MessageQueue *self) { return get_a_value(self->id, SVIFP_IPC_PERM_UID); } int mq_set_uid(MessageQueue *self, PyObject *py_value) { return set_a_value(self->id, SVIFP_IPC_PERM_UID, py_value); } PyObject * mq_get_gid(MessageQueue *self) { return get_a_value(self->id, SVIFP_IPC_PERM_GID); } int mq_set_gid(MessageQueue *self, PyObject *py_value) { return set_a_value(self->id, SVIFP_IPC_PERM_GID, py_value); } PyObject * mq_get_c_uid(MessageQueue *self) { return get_a_value(self->id, SVIFP_IPC_PERM_CUID); } PyObject * mq_get_c_gid(MessageQueue *self) { return get_a_value(self->id, SVIFP_IPC_PERM_CGID); } PyObject * mq_remove(int queue_id) { struct msqid_ds mq_info; DPRINTF("calling msgctl(...IPC_RMID...) on id %d\n", queue_id); if (-1 == msgctl(queue_id, IPC_RMID, &mq_info)) { switch (errno) { case EIDRM: case EINVAL: PyErr_Format(pExistentialException, "The queue no longer exists"); break; case EPERM: PyErr_SetString(pPermissionsException, "Permission denied"); break; default: PyErr_SetFromErrno(PyExc_OSError); break; } goto error_return; } Py_RETURN_NONE; error_return: return NULL; } void MessageQueue_dealloc(MessageQueue *self) { Py_TYPE(self)->tp_free((PyObject*)self); } PyObject * MessageQueue_new(PyTypeObject *type, PyObject *args, PyObject *keywords) { MessageQueue *self; self = (MessageQueue *)type->tp_alloc(type, 0); return (PyObject *)self; } int MessageQueue_init(MessageQueue *self, PyObject *args, PyObject *keywords) { int flags = 0; int mode = 0600; NoneableKey key; unsigned long max_message_size = QUEUE_MESSAGE_SIZE_MAX_DEFAULT; char *keyword_list[ ] = {"key", "flags", "mode", "max_message_size", NULL}; //MessageQueue(key, [flags = 0, [mode = 0600, [max_message_size = QUEUE_MESSAGE_SIZE_MAX_DEFAULT]]) if (!PyArg_ParseTupleAndKeywords(args, keywords, "O&|iik", keyword_list, convert_key_param, &key, &flags, &mode, &max_message_size)) goto error_return; if (max_message_size > QUEUE_MESSAGE_SIZE_MAX) { PyErr_Format(PyExc_ValueError, "The message length must be <= %lu\n", (unsigned long)QUEUE_MESSAGE_SIZE_MAX); goto error_return; } if ( !(flags & IPC_CREAT) && (flags & IPC_EXCL) ) { PyErr_SetString(PyExc_ValueError, "IPC_EXCL must be combined with IPC_CREAT"); goto error_return; } if (key.is_none && ((flags & IPC_EXCL) != IPC_EXCL)) { PyErr_SetString(PyExc_ValueError, "Key can only be None if IPC_EXCL is set"); goto error_return; } self->max_message_size = max_message_size; // I mask the caller's flags against the two IPC_* flags to ensure that // nothing funky sneaks into the flags. flags &= (IPC_CREAT | IPC_EXCL); mode &= 0777; if (key.is_none) { // (key == None) ==> generate a key for the caller do { errno = 0; self->key = get_random_key(); DPRINTF("Calling msgget, key=%ld, flags=0x%x\n", (long)self->key, flags); self->id = msgget(self->key, mode | flags); } while ( (-1 == self->id) && (EEXIST == errno) ); } else { // (key != None) ==> use key supplied by the caller self->key = key.value; DPRINTF("Calling msgget, key=%ld, flags=0x%x\n", (long)self->key, flags); self->id = msgget(self->key, mode | flags); } DPRINTF("id == %d\n", self->id); if (self->id == -1) { switch (errno) { case EACCES: PyErr_SetString(pPermissionsException, "Permission denied"); break; case EEXIST: PyErr_SetString(pExistentialException, "A queue with the specified key already exists"); break; case ENOENT: PyErr_SetString(pExistentialException, "No queue exists with the specified key"); break; case ENOMEM: PyErr_SetString(PyExc_MemoryError, "Not enough memory"); break; case ENOSPC: PyErr_SetString(PyExc_OSError, "The system limit for message queues has been reached"); break; default: PyErr_SetFromErrno(PyExc_OSError); break; } goto error_return; } return 0; error_return: return -1; } PyObject * MessageQueue_send(MessageQueue *self, PyObject *args, PyObject *keywords) { /* In Python >= 2.5, the Python argument specifier 's#' expects a py_ssize_t for its second parameter. A ulong is long enough to hold a py_ssize_t. It might be too big, though, on platforms where a long is larger than py_ssize_t. Therefore I *must* initialize it to 0 so that whatever Python doesn't write to is zeroed out. */ #if PY_MAJOR_VERSION > 2 static char args_format[] = "s*|Oi"; Py_buffer user_msg; #else static char args_format[] = "s#|Oi"; typedef struct { char *buf; long len; } MyBuffer; MyBuffer user_msg; user_msg.len = 0; #endif PyObject *py_block = NULL; int flags = 0; int type = 1; int rc; struct queue_message *p_msg = NULL; char *keyword_list[ ] = {"message", "block", "type", NULL}; // send(message, [block = True, [type = 1]]) if (!PyArg_ParseTupleAndKeywords(args, keywords, args_format, keyword_list, #if PY_MAJOR_VERSION > 2 &user_msg, #else &(user_msg.buf), &(user_msg.len), #endif &py_block, &type)) goto error_return; if (type <= 0) { PyErr_SetString(PyExc_ValueError, "The type must be > 0"); goto error_return; } if (user_msg.len > self->max_message_size) { PyErr_Format(PyExc_ValueError, "The message length exceeds queue's max_message_size (%lu)", self->max_message_size); goto error_return; } // default behavior (when py_block == NULL) is to block/wait. if (py_block && PyObject_Not(py_block)) flags |= IPC_NOWAIT; p_msg = (struct queue_message *)malloc(offsetof(struct queue_message, message) + user_msg.len); DPRINTF("p_msg is %p\n", p_msg); if (!p_msg) { PyErr_SetString(PyExc_MemoryError, "Out of memory"); goto error_return; } memcpy(p_msg->message, user_msg.buf, user_msg.len); p_msg->type = type; Py_BEGIN_ALLOW_THREADS DPRINTF("Calling msgsnd(), id=%ld, p_msg=%p, length=%lu, flags=0x%x\n", (long)self->id, p_msg, user_msg.len, flags); rc = msgsnd(self->id, p_msg, (size_t)user_msg.len, flags); Py_END_ALLOW_THREADS if (-1 == rc) { switch (errno) { case EACCES: PyErr_SetString(pPermissionsException, "Permission denied"); break; case EAGAIN: PyErr_SetString(pBusyException, "The queue is full, or a system-wide limit on the number of queue messages has been reached"); break; case EIDRM: PyErr_SetString(pExistentialException, "The queue no longer exists"); break; case EINTR: PyErr_SetString(pBaseException, "Signaled while waiting"); break; default: PyErr_SetFromErrno(PyExc_OSError); break; } goto error_return; } #if PY_MAJOR_VERSION > 2 PyBuffer_Release(&user_msg); #endif free(p_msg); Py_RETURN_NONE; error_return: #if PY_MAJOR_VERSION > 2 PyBuffer_Release(&user_msg); #endif free(p_msg); return NULL; } PyObject * MessageQueue_receive(MessageQueue *self, PyObject *args, PyObject *keywords) { PyObject *py_block = NULL; PyObject *py_return_tuple = NULL; int flags = 0; int type = 0; ssize_t rc; struct queue_message *p_msg = NULL; char *keyword_list[ ] = {"block", "type", NULL}; // receive([block = True, [type = 0]]) if (!PyArg_ParseTupleAndKeywords(args, keywords, "|Oi", keyword_list, &py_block, &type)) goto error_return; // default behavior (when py_block == NULL) is to block/wait. if (py_block && PyObject_Not(py_block)) flags |= IPC_NOWAIT; p_msg = (struct queue_message *)malloc(sizeof(struct queue_message) + self->max_message_size); DPRINTF("p_msg is %p, size = %lu\n", p_msg, sizeof(struct queue_message) + self->max_message_size); if (!p_msg) { PyErr_SetString(PyExc_MemoryError, "Out of memory"); goto error_return; } p_msg->type = type; Py_BEGIN_ALLOW_THREADS; rc = msgrcv(self->id, p_msg, (size_t)self->max_message_size, type, flags); Py_END_ALLOW_THREADS; DPRINTF("after msgrcv, p_msg->type=%ld, rc (size)=%ld\n", p_msg->type, (long)rc); if ((ssize_t)-1 == rc) { switch (errno) { case EACCES: PyErr_SetString(pPermissionsException, "Permission denied"); break; case EIDRM: case EINVAL: PyErr_SetString(pExistentialException, "The queue no longer exists"); break; case EINTR: PyErr_SetString(pBaseException, "Signaled while waiting"); break; case ENOMSG: PyErr_SetString(pBusyException, "No available messages of the specified type"); break; default: PyErr_SetFromErrno(PyExc_OSError); break; } goto error_return; } py_return_tuple = Py_BuildValue("NN", #if PY_MAJOR_VERSION > 2 PyBytes_FromStringAndSize(p_msg->message, rc), PyLong_FromLong(p_msg->type) #else PyString_FromStringAndSize(p_msg->message, rc), PyInt_FromLong(p_msg->type) #endif ); free(p_msg); return py_return_tuple; error_return: free(p_msg); return NULL; } PyObject * MessageQueue_remove(MessageQueue *self) { return mq_remove(self->id); } sysv_ipc-0.6.8/mq.h0000644000076500000240000000534312224412321013674 0ustar mestaff00000000000000#include // for definition of SSIZE_MAX typedef struct { PyObject_HEAD key_t key; int id; unsigned long max_message_size; } MessageQueue; /* Message queue message struct for send() & receive() On many systems this is defined in sys/msg.h already, but it's better for me to define it here. Name it something other than msgbuf to avoid conflict with the struct that the OS header file might define. */ struct queue_message { long type; char message[]; }; /* Maximum message size is limited by (a) the largest Python string I can create and (b) SSIZE_T_MAX. The latter restriction comes from the spec which says, "If the value of msgsz is greater than {SSIZE_MAX}, the result is implementation-defined." ref: http://www.opengroup.org/onlinepubs/000095399/functions/msgrcv.html */ #if SSIZE_MAX > PY_STRING_LENGTH_MAX #define QUEUE_MESSAGE_SIZE_MAX PY_STRING_LENGTH_MAX #else #define QUEUE_MESSAGE_SIZE_MAX SSIZE_MAX #endif /* The max message size is probably a very big number, and since a max-sized buffer is allocated every time receive() is called, it would be ugly if the default message size for new queues was the same as the max. In addition, many operating systems limit the entire queue to 2048 bytes, so defaulting the max message to something larger seems a bit stupid. This value is also present in numeric form in ReadMe.html, so if you change it here, change it there too. */ #define QUEUE_MESSAGE_SIZE_MAX_DEFAULT 2048 /* Object methods */ PyObject *MessageQueue_new(PyTypeObject *, PyObject *, PyObject *); int MessageQueue_init(MessageQueue *, PyObject *, PyObject *); void MessageQueue_dealloc(MessageQueue *); PyObject *MessageQueue_send(MessageQueue *, PyObject *, PyObject *); PyObject *MessageQueue_receive(MessageQueue *, PyObject *, PyObject *); PyObject *MessageQueue_remove(MessageQueue *); /* Object attributes (read-write & read-only) */ PyObject *mq_get_mode(MessageQueue *); int mq_set_mode(MessageQueue *, PyObject *); PyObject *mq_get_uid(MessageQueue *); int mq_set_uid(MessageQueue *, PyObject *); PyObject *mq_get_gid(MessageQueue *); int mq_set_gid(MessageQueue *, PyObject *); PyObject *mq_get_max_size(MessageQueue *); int mq_set_max_size(MessageQueue *, PyObject *); PyObject *mq_get_key(MessageQueue *); PyObject *mq_get_last_send_time(MessageQueue *); PyObject *mq_get_last_receive_time(MessageQueue *); PyObject *mq_get_last_change_time(MessageQueue *); PyObject *mq_get_last_send_pid(MessageQueue *); PyObject *mq_get_last_receive_pid(MessageQueue *); PyObject *mq_get_current_messages(MessageQueue *); PyObject *mq_get_c_uid(MessageQueue *); PyObject *mq_get_c_gid(MessageQueue *); PyObject *mq_str(MessageQueue *); PyObject *mq_repr(MessageQueue *); /* Misc. */ PyObject *mq_remove(int); sysv_ipc-0.6.8/PKG-INFO0000644000076500000240000000322612404600066014206 0ustar mestaff00000000000000Metadata-Version: 1.1 Name: sysv_ipc Version: 0.6.8 Summary: System V IPC primitives (semaphores, shared memory and message queues) for Python Home-page: http://semanchuk.com/philip/sysv_ipc/ Author: Philip Semanchuk Author-email: philip@semanchuk.com License: http://creativecommons.org/licenses/BSD/ Download-URL: http://semanchuk.com/philip/sysv_ipc/sysv_ipc-0.6.8.tar.gz Description: Sysv_ipc gives Python programs access to System V semaphores, shared memory and message queues. Most (all?) Unixes (including OS X) support System V IPC. Windows+Cygwin 1.7 might also work. Sample code is included. sysv_ipc is free software (free as in speech and free as in beer) released under a 3-clause BSD license. Complete licensing information is available in the LICENSE file. You might also be interested in the similar POSIX IPC module at: http://semanchuk.com/philip/posix_ipc/ Keywords: ipc inter-process communication semaphore shared memory shm message queue Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: BSD License Classifier: Operating System :: MacOS :: MacOS X Classifier: Operating System :: POSIX :: BSD :: FreeBSD Classifier: Operating System :: POSIX :: Linux Classifier: Operating System :: POSIX :: SunOS/Solaris Classifier: Operating System :: POSIX Classifier: Operating System :: Unix Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 3 Classifier: Topic :: Utilities sysv_ipc-0.6.8/prober/0000755000076500000240000000000012404600066014377 5ustar mestaff00000000000000sysv_ipc-0.6.8/prober/probe_page_size.c0000644000076500000240000000062312224412322017675 0ustar mestaff00000000000000//#define _XOPEN_SOURCE 500 #include "Python.h" #include // Code for determining page size swiped from Python's mmapmodule.c #if defined(HAVE_SYSCONF) && defined(_SC_PAGESIZE) static int my_getpagesize(void) { return sysconf(_SC_PAGESIZE); } #else #include #define my_getpagesize getpagesize #endif int main(void) { printf("%d\n", my_getpagesize()); return 0; } sysv_ipc-0.6.8/prober/semtimedop_test.c0000644000076500000240000000024212224412322017742 0ustar mestaff00000000000000//#define _XOPEN_SOURCE 500 #include "Python.h" #include #include int main(void) { semtimedop(0, NULL, 0, NULL); return 0; } sysv_ipc-0.6.8/prober/sniff_union_semun_defined.c0000644000076500000240000000023212224412322021736 0ustar mestaff00000000000000//#define _XOPEN_SOURCE 500 #include "Python.h" #include int main(void) { union semun foo; foo.val = 42; return 0; } sysv_ipc-0.6.8/prober.py0000644000076500000240000001301412404577701014761 0ustar mestaff00000000000000import os.path import os import subprocess import sys import distutils.sysconfig # Set these to None for 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 PYTHON_INCLUDE_DIR = os.path.dirname(distutils.sysconfig.get_config_h_filename()) #print (PYTHON_INCLUDE_DIR) 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): # Utility function that returns True if the file compiles and links # successfully, False otherwise. cmd = "cc -Wall -I%s -o ./prober/foo ./prober/%s" % \ (PYTHON_INCLUDE_DIR, filename) 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 -I%s -o ./prober/foo %s ./prober/%s" % \ (PYTHON_INCLUDE_DIR, 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 sniff_semtimedop(): return does_build_succeed("semtimedop_test.c") def sniff_union_semun_defined(): # AFAICT the semun union is supposed to be declared in one's code. # However, a lot of legacy code gets this wrong and some header files # define it, e.g.sys/sem.h on OS X where it's #ifdef-ed so that legacy # code won't break. On some systems, it appears and disappears based # on the #define value of _XOPEN_SOURCE. return does_build_succeed("sniff_union_semun_defined.c") def probe_semvmx(): # This is the hardcoded default that I chose for two reasons. First, # it's the default on my Mac so I know at least one system needs it # this low. Second, it fits neatly into a 16-bit signed int which # makes me hope that it's low enough to be safe on all systems. semvmx = 32767 # FIXME -- Ways to get SEMVMX -- # 1) Try to compile .c code assuming SEMVMX is #defined # 2) Parse the output from `sysctl kern.sysv.semvmx` (doesn't work # on OS X). # 3) Parse the output from `ipcs -S` # 4) Run .c code that loops, releasing a semaphore until semop() # returns ERANGE or the value goes wonky. OS X lets the latter # happen, which AFAICT is a violation of the spec. return semvmx def probe_page_size(): DEFAULT_PAGE_SIZE = 4096 page_size = compile_and_run("probe_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 probe(): d = { "KEY_MAX" : "LONG_MAX", "KEY_MIN" : "LONG_MIN" } conditionals = [ "_SEM_SEMUN_UNDEFINED" ] f = open("VERSION") version = f.read().strip() f.close() d["SYSV_IPC_VERSION"] = '"%s"' % version d["PAGE_SIZE"] = probe_page_size() if sniff_semtimedop(): d["SEMTIMEDOP_EXISTS"] = "" d["SEMAPHORE_VALUE_MAX"] = probe_semvmx() # Some (all?) Linux platforms #define _SEM_SEMUN_UNDEFINED if it's up # to my code to declare this union, so I use that flag as my standard. if not sniff_union_semun_defined(): d["_SEM_SEMUN_UNDEFINED"] = "" 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. To recreate this file, just delete it and re-run setup.py. KEY_MIN, KEY_MAX and SEMAPHORE_VALUE_MAX are stored internally in longs, so you should never #define them to anything larger than LONG_MAX. */ """ filename = "probe_results.h" if not os.path.exists(filename): lines = [ ] for key in d: if key in conditionals: lines.append("#ifndef %s" % key) lines.append("#define %s\t\t%s" % (key, d[key])) if key in conditionals: lines.append("#endif") # A trailing '\n' keeps compilers happy... f = open(filename, "w") f.write(msg + '\n'.join(lines) + '\n') f.close() return d if __name__ == "__main__": s = probe() print (s) sysv_ipc-0.6.8/README0000644000076500000240000000076012224412321013764 0ustar mestaff00000000000000Sysv_ipc gives Python programs access to System V semaphores, shared memory and message queues. Most (all?) Unixes (including OS X) support System V IPC. Windows+Cygwin 1.7 might also work. Sample code is included. sysv_ipc is free software (free as in speech and free as in beer) released under a 3-clause BSD license. Complete licensing information is available in the LICENSE file. You might also be interested in the similar POSIX IPC module at: http://semanchuk.com/philip/posix_ipc/ sysv_ipc-0.6.8/ReadMe.html0000644000076500000240000015615612404577662015166 0ustar mestaff00000000000000 System V IPC for Python - Semaphores, Shared Memory and Message Queues

System V IPC for Python - Semaphores, Shared Memory and Message Queues


RSS

This describes the sysv_ipc module which gives Python access to System V inter-process semaphores, shared memory and message queues on most (all?) *nix flavors. Examples include OS X, Linux, FreeBSD, OpenSolaris 2008.11, and AIX 5.2. It might also work under Windows with a library like Cygwin.

It works with Python 2.4 – 3.x. It's released under a BSD license.

You can download sysv_ipc version 0.6.8 ([md5 sum], [sha1 sum]) which contains the source code, setup.py, installation instructions and sample code. You can read about all of the changes in this version.

You might also want to read about some known bugs.

You might be interested in the very similar module posix_ipc which provides Python access to POSIX IPC primitives. POSIX IPC is a little easier to use than SysV IPC, but not all operating systems support it completely.

Module sysv_ipc

Jump to semaphores, shared memory, or message queues.

Module Functions

attach(id, [address = None, [flags = 0]])
Attaches the (existing) shared memory that has the given id and returns a new SharedMemory object. See SharedMemory.attach() for details on the address and flags parameters.

This method is useful only under fairly unusual circumstances. You probably don't need it.

ftok(path, id, [silence_warning = False])
Calls ftok(path, id). Note that ftok() has limitations, and this function will issue a warning to that effect unless silence_warning is True.
remove_semaphore(id)
Removes the semaphore with the given id.
remove_shared_memory(id)
Removes the shared memory with the given id.
remove_message_queue(id)
Removes the message queue with the given id.

Module Constants

IPC_CREAT, IPC_EXCL and IPC_CREX
IPC_CREAT and IPC_EXCL are flags used when creating IPC objects. They're bitwise unique and can be ORed together. IPC_CREX is shorthand for IPC_CREAT | IPC_EXCL.

When passed to an IPC object's constructor, IPC_CREAT indicates that you want to create a new object or open an existing one. If you want the call to fail if an object with that key already exists, specify the IPC_EXCL flag, too.

IPC_PRIVATE
This is a special value that can be passed in place of a key. It implies that the IPC object should be available only to the creating process or its child processes (e.g. those created with fork()).
KEY_MIN and KEY_MAX
Denote the range of keys that this module accepts. Your OS might limit keys to a smaller range depending on the typedef of key_t.

Keys randomly generated by this module are in the range 1 <e; key <e; SHRT_MAX. That's type-safe unless your OS has a very bizarre definition of key_t.

SEMAPHORE_VALUE_MAX
The maximum value of a semaphore.
PAGE_SIZE
The operating system's memory page size, in bytes. It's probably a good idea to make shared memory segments some multiple of this size.
SEMAPHORE_TIMEOUT_SUPPORTED
True if the platform supports timed semaphore waits, False otherwise.
SHM_RND
You probably don't need this, but it can be used when attaching shared memory to force the address to be rounded down to SHMLBA. See your system's man page for shmat() for more information.
SHM_HUGETLB, SHM_NORESERVE and SHM_REMAP
You probably don't need these. They're Linux-specific flags that can be passed to the SharedMemory constructor, or to the .attach() function in the case of SHM_REMAP. See your system's man page for shmget() and shmat() for more information.

Module Errors

In addition to standard Python errors (e.g. ValueError), this module raises custom errors. These errors cover situations specific to IPC.

Error
The base error class for all the custom errors in this module. This error is occasionally raised on its own but you'll almost always see a more specific error.
InternalError
Indicates that something has gone very wrong in the module code. Please report this to the maintainer.
PermissionsError
Indicates that you've attempted something that the permissions on the IPC object don't allow.
ExistentialError
Indicates an error related to the existence or non-existence of an IPC object.
BusyError
Raised when a semaphore call to .P() or .Z() either times out or would be forced to wait when its block attribute is False.
NotAttachedError
Raised when a process attempts to read from or write to a shared memory segment to which it is not attached.

The Semaphore Class

This is a handle to a semaphore.

Methods

Semaphore(key, [flags = 0, [mode = 0600, [initial_value = 0]]])
Creates a new semaphore or opens an existing one.

key must be None, IPC_PRIVATE or an integer > KEY_MIN and ≤ KEY_MAX. If the key is None, the module chooses a random unused key.

The flags specify whether you want to create a new semaphore or open an existing one.

  • With flags set to the default of 0, the module attempts to open an existing semaphore identified by key and raises a ExistentialError if that semaphore doesn't exist.
  • With flags set to IPC_CREAT, the module opens the semaphore identified by key or creates a new one if no such semaphore exists. Using IPC_CREAT by itself is not recommended. (See Semaphore Initialization.)
  • With flags set to IPC_CREX (IPC_CREAT | IPC_EXCL), the module creates a new semaphore identified by key. If a semaphore with that key already exists, the call raises an ExistentialError. The initial_value is ignored unless both of these flags are specified or if the semaphore is read-only.

When opening an existing semaphore, mode is ignored.

acquire([timeout = None, [delta = 1]])
Waits (conditionally) until the semaphore's value is > 0 and then returns, decrementing the semaphore.

The timeout (which can be a float) specifies how many seconds this call should wait, if at all.

The semantics of the timeout changed a little in version 0.3.

  • A timeout of None (the default) implies no time limit. The call will not return until its wait condition is satisfied.
  • When timeout is 0, the call raises a BusyError if it can't immediately acquire the semaphore. Since it will return immediately if not asked to wait, this can be thought of as "non-blocking" mode.
  • When the timeout is > 0, the call will wait no longer than timeout seconds before either returning (having acquired the semaphore) or raising a BusyError.

When the call returns, the semaphore's value decreases by delta (or more precisely, abs(delta)) which defaults to 1.

On platforms that don't support the semtimedop() API call, all timeouts (including zero) are treated as infinite. The call will not return until its wait condition is satisfied.

Most platforms provide semtimedop(). OS X is a notable exception. The module's Boolean constant SEMAPHORE_TIMEOUT_SUPPORTED is True on platforms that support semtimedop().

release([delta = 1])
Releases (increments) the semaphore.

The semaphore's value increases by delta (or more precisely, abs(delta)) which defaults to 1.

P()
A synonym for .acquire() that takes the same parameters.

"P" stands for prolaag or probeer te verlagen (try to decrease), the original name given by Edsger Dijkstra.

V()
A synonym for .release() that takes the same parameters.

"V" stands for verhoog (increase), the original name given by Edsger Dijkstra.

Z([timeout = None])
Blocks until zee zemaphore is zero.

Timeout has the same meaning as described in .acquire().

remove()
Removes (deletes) the semaphore from the system.

As far as I can tell, the effect of deleting a semaphore that other processes are still using is OS-dependent. Check your system's man pages for semctl(IPC_RMID).

Attributes

key (read-only)
The key passed in the call to the constructor.
id (read-only)
The id assigned to this semaphore by the OS.
value
The integer value of the semaphore.
undo
Defaults to False.

When True, operations that change the semaphore's value will be undone (reversed) when the process exits. Note that when a process exits, an undo operation may imply that a semaphore's value should become negative or exceed its maximum. Behavior in this case is system-dependent, which means that using this flag can make your code non-portable.

block
Defaults to True, which means that calls to acquire() and release() will not return until their wait conditions are satisfied.

When False, these calls will not block but will instead raise an error if they are unable to return immediately.

mode
The semaphore's permission bits.

Tip: the following Python code will display the mode in octal:
print int(str(my_sem.mode), 8)

uid
The semaphore's user id.
gid
The semaphore's group id.
cuid (read-only)
The semaphore creator's user id.
cgid (read-only)
The semaphore creator's group id.
last_pid (read-only)
The PID of the process that last called semop() (.P(), .V() or .Z()) on this semaphore.
waiting_for_nonzero (read-only)
The number of processes waiting for the value of the semaphore to become non-zero (i.e. the number waiting in a call to .P()).
waiting_for_zero (read-only)
The number of processes waiting for the value of the semaphore to become zero (i.e. the number waiting in a call to .Z()).
o_time (read-only)
The last time semop() (i.e. .P(), .V() or .Z()) was called on this semaphore.

Context Manager Support

These semaphores provide __enter__() and __exit__() methods so they can be used in context managers. For instance --

with sysv_ipc.Semaphore(name) as sem:
    # Do something...

Entering the context acquires the semaphore, exiting the context releases the semaphore. See demo4/child.py for a complete example.

The SharedMemory Class

This is a handle to a shared memory segment.

Methods

SharedMemory(key, [flags = 0, [mode = 0600, [size = 0 or PAGE_SIZE, [init_character = ' ']]]])
Creates a new shared memory segment or opens an existing one. The memory is automatically attached.

key must be None, IPC_PRIVATE or an integer > 0 and ≤ KEY_MAX. If the key is None, the module chooses a random unused key.

The flags specify whether you want to create a new shared memory segment or open an existing one.

  • With flags set to the default of 0, the module attempts to open an existing shared memory segment identified by key and raises a ExistentialError if it doesn't exist.
  • With flags set to IPC_CREAT, the module opens the shared memory segment identified by key or creates a new one if no such segment exists. Using IPC_CREAT by itself is not recommended. (See Memory Initialization.)
  • With flags set to IPC_CREX (IPC_CREAT | IPC_EXCL), the module creates a new shared memory segment identified by key. If a segment with that key already exists, the call raises a ExistentialError.

    When both IPC_CREX is specified and the caller has write permission, each byte in the new memory segment will be initialized to the value of init_character.

The value of size depends on whether one is opening an existing segment or creating a new one.

  • When opening an existing segment, size must be ≤ the existing segment's size. Zero is always valid.
  • When creating an new segment, many (most? all?) operating systems insist on a size > 0. In addition, some round the size up to the next multiple of PAGE_SIZE.

This module supplies a default size of PAGE_SIZE when IPC_CREX is specified and 0 otherwise.

attach([address = None, [flags = 0]])
Attaches this process to the shared memory. The memory must be attached before calling .read() or .write(). Note that the constructor automatically attaches the memory so you won't need to call this method unless you explicitly detach it and then want to use it again.

The address parameter allows one to specify (as a Python long) a memory address at which to attach the segment. Passing None (the default) is equivalent to passing NULL to shmat(). See that function's man page for details.

The flags are mostly only relevant if one specifies a specific address. One exception is the flag SHM_RDONLY which, surprisingly, attaches the segment read-only.

Note that on some (and perhaps all) platforms, each call to .attach() increments the system's "attached" count. Thus, if each call to .attach() isn't paired with a call to .detach(), the system's "attached" count for the shared memory segment will not go to zero when the process exits. As a result, the shared memory segment may not disappear even when its creator calls .remove() and exits.

detach()
Detaches this process from the shared memory.
read([byte_count = 0, [offset = 0]])
Reads up to byte_count bytes from the shared memory segment starting at offset and returns them as a string under Python 2 or as a bytes object under Python 3.

If byte_count is zero (the default) the entire buffer is returned.

This method will never attempt to read past the end of the shared memory segment, even when offset + byte_count exceeds the memory segment's size. In that case, the bytes from offset to the end of the segment are returned.

write(s, [offset = 0])
Writes the string s to the shared memory, starting at offset.

At most n bytes will be written, where n = the segment's size minus offset.

The string may contain embedded NULL bytes ('\0').

remove()
Removes (destroys) the shared memory. Note that actual destruction of the segment only occurs when all processes have detached.

Attributes

key (read-only)
The key provided in the constructor.
id (read-only)
The id assigned to this semaphore by the OS.
size (read-only)
The size of the segment in bytes.
address (read-only)
The address of the segment as Python long.
attached (read-only)
If True, this segment is currently attached.
last_attach_time (read-only)
The last time a process attached this segment.
last_detach_time (read-only)
The last time a process detached this segment.
last_change_time (read-only)
The last time a process changed the uid, gid or mode on this segment.
creator_pid (read-only)
The PID of the process that created this segment.
last_pid (read-only)
The PID of the most last process to attach or detach this segment.
number_attached (read-only)
The number of processes attached to this segment.
uid
The segment's user id.
gid
The segment's group id.
mode
The shared memory's permission bits.

Tip: the following Python code will display the mode in octal:
print int(str(my_mem.mode), 8)

cuid (read-only)
The segment creator's user id.
cgid (read-only)
The segment creator's group id.

The MessageQueue Class

This is a handle to a message queue.

Methods

MessageQueue(key, [flags = 0, [mode = 0600, [max_message_size = 2048]]])
Creates a new message queue or opens an existing one.

key must be None, IPC_PRIVATE or an integer > 0 and ≤ KEY_MAX. If the key is None, the module chooses a random unused key.

The flags specify whether you want to create a new queue or open an existing one.

  • With flags set to the default of 0, the module attempts to open an existing message queue identified by key and raises a ExistentialError if it doesn't exist.
  • With flags set to IPC_CREAT, the module opens the message queue identified by key or creates a new one if no such queue exists.
  • With flags set to IPC_CREX (IPC_CREAT | IPC_EXCL), the module creates a new message queue identified by key. If a queue with that key already exists, the call raises a ExistentialError.

The max_message_size can be increased from the default, but be aware of the issues discussed in Message Queue Limits.

send(message, [block = True, [type = 1]])
Puts a message on the queue.

The message string can contain embedded NULLs (ASCII 0x00).

The block flag specifies whether or not the call should wait if the message can't be sent (if, for example, the queue is full). When block is False, the call will raise a BusyError if the message can't be sent immediately.

The type is associated with the message and is relevant when calling receive(). It must be > 0.

receive([block = True, [type = 0]])
Receives a message from the queue, returning a tuple of (message, type). Under Python 3, the message is a bytes object.

The block flag specifies whether or not the call should wait if there's no messages of the specified type to retrieve. When block is False, the call will raise a BusyError if a message can't be received immediately.

The type permits some control over which messages are retrieved.

  • When type == 0, the call returns the first message on the queue regardless of its type.
  • When type > 0, the call returns the first message of that type.
  • When type < 0, the call returns the first message of the lowest type that is ≤ the absolute value of type.
remove()
Removes (deletes) the message queue.

Attributes

key (read-only)
The key provided in the constructor.
id (read-only)
The id assigned to this queue by the OS.
max_size
The maximum size of the queue in bytes. Only a process with "appropriate privileges" can increase this value, and on some systems even that won't work. See Message Queue Limits for details.
last_send_time (read-only)
The last time a message was placed on the queue.
last_receive_time (read-only)
The last time a message was received from the queue.
last_change_time (read-only)
The last time a process changed the queue's attributes.
last_send_pid (read-only)
The id of the most recent process to send a message.
last_receive_pid (read-only)
The id of the most recent process to receive a message.
current_messages (read-only)
The number of messages currently in the queue.
uid
The queue's user id.
gid
The queue's group id.
mode
The queue's permission bits.

Tip: the following Python code will display the mode in octal:
print int(str(my_mem.mode), 8)

cuid (read-only)
The queue creator's user id.
cgid (read-only)
The queue creator's group id.

Supported Features and Differences from SHM

This module is almost, but not quite, a superset of shm. Some of the additional features are the ability to override the block flag on a per-call basis, the ability to change the semaphore's value in increments > 1 when calling .P() and .V() and exposure of sem_otime.

Differences that might trip you up are listed below.

  • Shm compiles under Python 2.3 and older; this module has not been tested with Python older than 2.4
  • Attribute names and method signatures are different.
  • This module offers neither the functions semaphore_haskey() nor memory_haskey().
  • This module's default permission on objects is 0600 as opposed to shm's 0666.
  • Shm maintained an internal dictionary of semaphores and shared memory segments. The object keys served as the dictionary keys. If you asked for the same object multiple times, shm would return the same Python object. I'm not convinced this was safe, particularly in the case where an object may have been destroyed and another with the same key created in its place.

Usage Tips

Sample Code

This module comes with two demonstration apps. The first (in the directory demo) shows how to use shared memory and semaphores. The second (in the directory demo2) shows how to use message queues.

The Weakness of ftok()

Most System V IPC sample code recommends ftok() for generating an integer key that's more-or-less random. It does not, however, guarantee that the key it generates is unused. If ftok() gives your application a key that some other application is already using, your app is in trouble unless it has a reliable second mechanism for generating a key. And if that's the case, why not just abandon ftok() and use the second mechanism exclusively?

This is the weakness of ftok() -- it isn't guaranteed to give you what you want. The BSD man page for ftok says it is "quite possible for the routine to return duplicate keys". The term "quite possible" isn't quantified, but suppose it means one-tenth of one percent. Who wants to have 1-in-1000 odds of a catastrophic failure in their program, or even 1-in-10000?

This module obviates the need for ftok() by generating random keys for you. If your application can't use sysv_ipc's automatically generated keys because it needs to know the key in advance, hardcoding a random number like 123456 in your app might be no worse than using ftok() and has the advantage of not hiding its limitations.

This module provides ftok() in case you want to experiment with it. However, to emphasize its weakness, this version of ftok() raises a warning with every call unless you explicitly pass a flag to silence it.

This package also provides ftok_experiment.py so that you can observe how often ftok() generates duplicate keys on your system.

Semaphore Initialization

When a System V sempahore is created at the C API level, the OS is not required to initialize the semaphore's value. (This per the SUSv3 standard for semget().) Some (most? all?) operating systems initialize it to zero, but this behavior is non-standard and therefore can't be relied upon.

If sempahore creation happens in an predictable, orderly fashion, this isn't a problem. But a race condition arises when multiple processes vie to create/open the same semaphore. The problem lies in the fact that when an application calls semget() with only the IPC_CREAT flag, the caller can't tell whether or not he has created a new semaphore or opened an existing one. This makes it difficult to create reliable code without using IPC_EXCL. W. Richard Stevens' Unix Network Programming Volume 2 calls this "a fatal flaw in the design of System V semaphores" (p 284).

For instance, imagine processes P1 and P2. They're executing the same code, and that code intends to share a binary semaphore. Consider the following sequence of events at the startup of P1 and P2 –

  1. P1 calls semget(IPC_CREAT) to create the semaphore S.
  2. P2 calls semget(IPC_CREAT) to open S.
  3. P1 initializes the semaphore's value to 1.
  4. P1 calls acquire(), decrementing the value to 0.
  5. P2, assuming S is a newly-created semaphore that needs to be initialized, incorrectly sets the semaphore's value to 1.
  6. P2 calls acquire(), decrementing the value to 0. Both processes now think they own the lock.

W. Richard Stevens' solution for this race condition is to check the value of sem_otime (an element in the semid_ds struct that's populated on the call to semctl(IPC_STAT) and which is exposed to Python by this module) which is initialized to zero when the semaphore is created and otherwise holds the time of the last call to semop() (which is called by P()/acquire(), V()/release(), and Z()).

In Python, each process would run something like this:

try:
    sem = sysv_ipc.Semaphore(42, sysv_ipc.IPC_CREX)
except sysv_ipc.ExistentialError:
    # One of my peers created the semaphore already
    sem = sysv_ipc.Semaphore(42)
    # Waiting for that peer to do the first acquire or release
    while not sem.o_time:
        time.sleep(.1)
else:
    # Initializing sem.o_time to nonzero value
    sem.release()
# Now the semaphore is safe to use.

Shared Memory Initialization

With shared memory, using the IPC_CREAT flag without IPC_EXCL is problematic unless you know the size of the segment you're potentially opening.

Why? Because when creating a new segment, many (most? all?) operating systems demand a non-zero size. However, when opening an existing segment, zero is the only guaranteed safe value (again, assuming one doesn't know the size of the segment in advance). Since IPC_CREAT can open or create a segment, there's no safe value for the size under this circumstance.

As a (sort of) side note, the SUSv3 specification for shmget() says only that the size of a new segment must not be less than "the system-imposed minimum". I gather that at one time, some systems set the minimum at zero despite the fact that it doesn't make much sense to create a zero-length shared memory segment. I think most modern systems do the sensible thing and insist on a minimum length of 1.

Message Queue Limits

Python programmers can usually remain blissfully ignorant of memory allocation issues. Unfortunately, a combination of factors makes them relevant when dealing with System V message queues.

Some implementations impose extremely stingy limits. For instance, many BSDish systems (OS X, FreeBSD, NetBSD, and OpenBSD) limit queues to 2048 bytes. Note that that's the total queue size, not the message size. Two 1k messages would fill the queue.

Those limits can be very difficult to change. At best, only privileged processes can increase the limit. At worst, the limit is a kernel parameter and requires a kernel change via a tunable or a recompile.

This module can't figure out what the limits are, so it can't cushion them or even report them to you. On some systems the limits are expressed in header files, on others they're available through kernel interfaces (like FreeBSD's sysctl). Under OS X and to some extent OpenSolaris I can't figure out where they're defined and what I report here is the result of experimentation and educated guesses formed by Googling.

The good news is that this module will still behave as advertised no matter what these limits are. Nevertheless you might be surprised when a call to .send() get stuck because a queue is full even though you've only put 2048 bytes of messages in it.

Here are the limits I've been able to find under my test operating systems, ordered from best (most generous) to worst (most stingy). This information was current as of 2009 when I wrote the message queue code. It's getting pretty stale now. I hope the situation has improved over the 2009 numbers I describe below.

Under OpenSolaris 2008.05 each queue's maximum size defaults to 64k. A privileged process (e.g. root) can change this through the max_size attribute of a sysv_ipc.MessageQueue object. I was able to increase it to 16M and successfully sent sixteen 1M messages to the queue.

Under Ubuntu 8.04 (and perhaps other Linuxes) each queue's maximum size defaults to 16k. As with OpenSolaris, I was able to increase this to 16M, but only for a privileged process.

Under FreeBSD 7 and I think NetBSD and OpenBSD, each queue's maximum size defaults to 2048 bytes. Furthermore, one can (as root) set max_size to something larger and FreeBSD doesn't complain, but it also ignores the change.

OS X is the worst of the lot. Each queue is limited to 2048 bytes and OS X silently ignores attempts to increase this (just like FreeBSD). To add insult to injury, there appears to be no way to increase this limit short of recompiling the kernel. I'm guessing at this based on the Darwin message queue limits.

If you want to search for these limits on your operating system, the key constants are MSGSEG, MSGSSZ, MSGTQL, MSGMNB, MSGMNI and MSGMAX. Under BSD, sysctl kern.ipc should tell you what you need to know and may allow you to change these parameters.

Nobody Likes a Mr. Messy

Semaphores and especially shared memory are a little different from most Python objects and therefore require a little more care on the part of the programmer. When a program creates a semaphore or shared memory object, it creates something that resides outside of its own process, just like a file on a hard drive. It won't go away when your process ends unless you explicitly remove it.

In short, remember to clean up after yourself.

Consult Your Local man Pages

The sysv_ipc module is just a wrapper around your system's API. If your system's implementation has quirks, the man pages for semget, semctl, semop shmget, shmat, shmdt and shmctl will probably cover them.

Interesting Tools

Many systems (although not some older versions of OS X) come with ipcs and ipcrm. The former shows existing shared memory, semaphores and message queues on your system and the latter allows you to remove them.

Last But Not Least

For Pythonistas –

Known Bugs

Bugs? My code never has bugs! There are, however, some suboptimal anomalies...

Version History

  • Current – 0.6.8 (12 Sept 2014) –
    • Fixed a bug in prober.py where prober would fail on systems where Python's include file was not named pyconfig.h. (RHEL 6 is one such system.) Thanks to Joshua Harlow for the bug report and patch.
    • Spasibo again to Yuriy Taraday for pointing out that the semaphore initialization example I changed in the previous version was still not quite correct, and for suggesting something better.
  • 0.6.7 (1 August 2014) –

    Spasibo to Yuriy Taraday for reporting some doc errors corrected in this version.

    • Added KEY_MIN as a module-level constant. The documentation has claimed that it was available for a long time so now the code finally matches the documentation.
    • Changed randomly generated keys to never use a value of 0.
    • Fixed two documenation errors in the special section on semaphore initialization and gave long overdue credit to W. Richard Stevens' book for the code idea. (Any code mistakes are mine, not his.)
    • This is the first version downloadable from PyPI.
  • 0.6.6 (15 October 2013) –

    Added the ability to use Semaphores in context managers. Thanks to Matt Ruffalo for the suggestion and patch.

  • 0.6.5 (31 March 2013) –

    Fixed a bug where SharedMemory.write() claimed to accept keyword arguments but didn't actually do so. Thanks to Matt Ruffalo for the bug report and patch.

  • 0.6.4 (19 Dec 2012) –
    • Added a module-level attach() method based on a suggestion by Vladimír Včelák.
    • Added the ftok() method along with dire warnings about its use.
  • 0.6.3 (3 Oct 2010) –
    • Fixed a segfault in write() that occurred any time an offset was passed. This was introduced in v0.6.0. Tack to Johan Bernhardtson for the bug report.
  • 0.6.2 (6 July 2010) –
    • Updated setup.py metadata to reflect Python 3 compatibility. No functional change.
  • 0.6.1 (26 June 2010) –
    • Fixed a typo introduced in the previous version that caused unpredictable behavior (usually a segfault) if a block flag was passed to MessageQueue.send(). Obrigado to Álvaro Justen and Ronald Kaiser for the bug report and patch.
  • 0.6.0 (20 May 2010) –
    • Added Python 3 support.
    • Renamed a constant that caused a problem under AIX. Thanks to Loic Nageleisen for the bug report.
    • Updated this documentation a little.
  • 0.5.2 (17 Jan 2010)
    • Fixed a bug that insisted on keys > 0. Thanks to 原志 (Daniel) for the bug report and patch.
    • Fixed a bug where the module could have generated invalid keys on systems that typedef key_t as a short. I don't think such a system exists, though.
    • Fixed a LICENSE file typo.
    • Added an RSS feed to this page.
  • 0.5.1 (1 Dec 2009) –

    No code changes in this version.

    • Fixed the comment in sysv_ipc_module.c that still referred to the GPL license.
    • Added the attributes __version, __author__, __license__ and __copyright__.
    • Removed file() from setup.py in favor of open().
  • 0.5 (6 Oct 2009) –

    No code changes in this version.

    • Changed the license from GPL to BSD.
  • 0.4.2 (22 Mar 2009) –

    No code changes in this version.

    • Fixed broken documentation links to youtube.
    • Fixed the project name in the LICENSE file.
  • 0.4.1 (12 Feb 2009) –
    • Changed status to beta.
    • Added automatic generation of keys.
    • Added a message queue demo.
    • Added str() and repr() support to all objects.
    • Fixed a bug in SharedMemory.write() that could cause a spurious error of "Attempt to write past end of memory segment". This bug only occurred on platforms using different sizes for long and int, or long and py_size_t (depending on Python version). Tack to Jesper for debug help.
    • Plugged a memory leak in MessageQueue.receive().
    • Added a VERSION attribute to the module.
  • 0.4 (28 Jan 2009) –
    • Added message queues.
    • Fixed a bug where the key attribute of SharedMemory objects returned garbage.
    • Fixed a bug where keys > INT_MAX would get truncated on platforms where longs are bigger than ints.
    • Provided decent inline documentation for object attributes (available via the help() command).
    • Rearranged the code which was suffering growing pains.
  • 0.3 (16 Jan 2009) –

    This version features a rename of classes and errors (sorry about breaking the names), some modifications to semaphore timeouts, and many small fixes.

    A semaphore's .block flag now consistently trumps the timeout. When False, the timeout is irrelevant -- calls will never block. In prior versions, the flag was ignored when the timeout was non-zero.

    Also, on platforms (such as OS X) where semtimedop() is not supported, all timeouts are now treated as None. In other words, when .block is True, all calls wait as long as necessary.

    Other fixes –

    • Fixed the poor choices I'd made for class and error names by removing the leading "SysV" and "SysVIpc".
    • Removed dependencies on Python 2.5. The module now works with Python 2.4.4 and perhaps earlier versions.
    • Fixed a bug where I was not following SysV semaphore semantics for interaction between timeouts and the block flag.
    • Fixed compile problems on OpenSolaris 2008.05.
    • Got rid of OS X compile warnings.
    • Fixed many instances where I was making potentially lossy conversions between Python values and Unix-specific types like key_t, pid_t, etc.
    • Fixed a bug in the SharedMemory attach() function that would set an error string but not return an error value if the passed address was not None or a long.
    • Simplified the code a little.
    • Restricted the semaphore acquire() and release() delta to be between SHORT_MIN and SHORT_MAX since it is a short in the SUSv2 spec.
    • Fixed a bug where calling acquire() or release() with a delta of 0 would call .Z() instead.
    • Disallowed byte counts ≤ 0 in SharedMemory.read()
    • Fixed a bug in the SharedMemory init code that could, under vanishingly rare circumstances, return failure without setting the error code.
    • Removed some dead code relating to the .seq member of the sem_perm and shm_perm structs.
    • Stopped accessing the non-standard key (a.k.a. _key and __key) element of the ipc_perm struct.
    • Added code to ensure the module doesn't try to create a string that's larger than Python permits when reading from shared memory.
  • 0.2.1 (3 Jan 2009) –
    • Fixed a bug that prevented the module-specific errors (ExistentialError, etc.) from being visible in the module.
    • Fixed a bug that re-initialized shared memory with the init character when only IPC_CREAT was specified and an existing segment was opened.
    • Fixed a bug that always defaulted the size of a shared memory segment to PAGE_SIZE. Updated code and documentation to use intelligent defaults. Tack to Jesper for the bug report.
    • Several cosmetic changes. (Added more metadata to setup.py, added a newline to the end of probe_results.h to avoid compiler complaints, etc.)
  • 0.2 (16 Dec 2008) – Lots of small fixes.
    • Fixed a bug where calling shm.remove() on shared memory that was already removed would cause a SystemError. (I wasn't setting the Python error before returning.)
    • Fixed a couple of bugs that would cause the creation of a new, read-only shared memory segment to fail.
    • Fixed a bug that would cause the creation of a new, read-only semaphore to fail.
    • Added the constant IPC_CREX.
    • Renamed (sorry) MAX_KEY to KEY_MAX and MAX_SEMAPHORE_VALUE to SEMAPHORE_VALUE_MAX to be consistent with the C naming convention in limits.h.
    • Hardcoded SEMAPHORE_VALUE_MAX to 32767 until I can find a reliable way to determine it at install time.
    • Changed prober.py to write out a C header file with platform-specific definitions in it.
    • Replaced OSError in shared memory functions with custom errors from this module.
    • Added code to raise a ValueError when an attempt is made to assign an out-of-range value to a semaphore.
    • Added code to raise a ValueError when an out-of-range key is passed to a constructor.
    • Fixed a bug in the demo program conclusion.c that caused some informational messages to be printed twice.
    • Fixed some documentation bugs.
  • 0.1 (4 Dec 2008) – Original (alpha) version.

Future Features/Changes

These are features that may or may not be added depending on technical difficulty, user interest and so forth.

  • Update this documentation with a list of platforms that support semtimedop().
  • Find a way to make SEMAPHORE_VALUE_MAX more accurate.

I don't plan on adding support for semaphore sets.

sysv_ipc-0.6.8/semaphore.c0000644000076500000240000005047412224532727015257 0ustar mestaff00000000000000#include "Python.h" #include "structmember.h" #include "common.h" #include "semaphore.h" #define ONE_BILLION 1000000000 // This enum has to start at zero because its values are used as an // arry index in sem_perform_semop(). enum SEMOP_TYPE { SEMOP_P = 0, SEMOP_V, SEMOP_Z }; /* Struct to contain a timeout which can be None */ typedef struct { int is_none; int is_zero; struct timespec timestamp; } NoneableTimeout; // It is recommended practice to define this union here in the .c module, but // it's been common practice for platforms to define it themselves in header // files. For instance, BSD and OS X do so (provisionally) in sem.h. As a // result, I need to surround this with an #ifdef. #ifdef _SEM_SEMUN_UNDEFINED union semun { int val; /* used for SETVAL only */ struct semid_ds *buf; /* for IPC_STAT and IPC_SET */ unsigned short *array; /* used for GETALL and SETALL */ }; #endif static int convert_timeout(PyObject *py_timeout, void *converted_timeout) { // Converts a PyObject into a timeout if possible. The PyObject should // be None or some sort of numeric value (e.g. int, float, etc.) // converted_timeout should point to a NoneableTimeout. When this function // returns, if the NoneableTimeout's is_none is true, then the rest of the // struct is undefined. Otherwise, the rest of the struct is populated. int rc = 0; double simple_timeout = 0; NoneableTimeout *p_timeout = (NoneableTimeout *)converted_timeout; // The timeout can be None or any Python numeric type (float, // int, long). if (py_timeout == Py_None) rc = 1; else if (PyFloat_Check(py_timeout)) { rc = 1; simple_timeout = PyFloat_AsDouble(py_timeout); } #if PY_MAJOR_VERSION < 3 else if (PyInt_Check(py_timeout)) { rc = 1; simple_timeout = (double)PyInt_AsLong(py_timeout); } #endif else if (PyLong_Check(py_timeout)) { rc = 1; simple_timeout = (double)PyLong_AsLong(py_timeout); } // The timeout may not be negative. if ((rc) && (simple_timeout < 0)) rc = 0; if (!rc) PyErr_SetString(PyExc_TypeError, "The timeout must be None or a non-negative number"); else { if (py_timeout == Py_None) p_timeout->is_none = 1; else { p_timeout->is_none = 0; p_timeout->is_zero = (!simple_timeout); // Note the difference between this and POSIX timeouts. System V // timeouts expect tv_sec to represent a delta from the current // time whereas POSIX semaphores expect an absolute value. p_timeout->timestamp.tv_sec = (time_t)floor(simple_timeout); p_timeout->timestamp.tv_nsec = (long)((simple_timeout - floor(simple_timeout)) * ONE_BILLION); } } return rc; } PyObject * sem_str(Semaphore *self) { #if PY_MAJOR_VERSION > 2 return PyUnicode_FromFormat("Key=%ld, id=%d", (long)self->key, self->id); #else return PyString_FromFormat("Key=%ld, id=%d", (long)self->key, self->id); #endif } PyObject * sem_repr(Semaphore *self) { #if PY_MAJOR_VERSION > 2 return PyUnicode_FromFormat("sysv_ipc.Semaphore(%ld)", (long)self->key); #else return PyString_FromFormat("sysv_ipc.Semaphore(%ld)", (long)self->key); #endif } static void sem_set_error(void) { switch (errno) { case ENOENT: case EINVAL: PyErr_SetString(pExistentialException, "No semaphore exists with the specified key"); break; case EEXIST: PyErr_SetString(pExistentialException, "A semaphore with the specified key already exists"); break; case EACCES: PyErr_SetString(pPermissionsException, "Permission denied"); break; case ERANGE: PyErr_Format(PyExc_ValueError, "The semaphore's value must remain between 0 and %ld (SEMAPHORE_VALUE_MAX)", (long)SEMAPHORE_VALUE_MAX); break; case EAGAIN: PyErr_SetString(pBusyException, "The semaphore is busy"); break; case EIDRM: PyErr_SetString(pExistentialException, "The semaphore was removed"); break; case EINTR: PyErr_SetString(pBaseException, "Signaled while waiting"); break; case ENOMEM: PyErr_SetString(PyExc_MemoryError, "Not enough memory"); break; default: PyErr_SetFromErrno(PyExc_OSError); break; } } static PyObject * sem_perform_semop(enum SEMOP_TYPE op_type, Semaphore *self, PyObject *args, PyObject *keywords) { int rc = 0; NoneableTimeout timeout; struct sembuf op[1]; /* delta (a.k.a. struct sembuf.sem_op) is a short ref: http://www.opengroup.org/onlinepubs/000095399/functions/semop.html */ short int delta; char *keyword_list[3][3] = { {"timeout", "delta", NULL}, // P == acquire {"delta", NULL}, // V == release {"timeout", NULL} // Z == zero test }; /* Initialize this to the default value. If the user doesn't pass a timeout, Python won't call convert_timeout() and so the timeout will be otherwise uninitialized. */ timeout.is_none = 1; /* op_type is P, V or Z corresponding to the 3 Semaphore methods that call that call semop(). */ switch (op_type) { case SEMOP_P: // P == acquire delta = -1; rc = PyArg_ParseTupleAndKeywords(args, keywords, "|O&h", keyword_list[SEMOP_P], convert_timeout, &timeout, &delta); if (rc && !delta) { rc = 0; PyErr_SetString(PyExc_ValueError, "The delta must be non-zero"); } else delta = -abs(delta); break; case SEMOP_V: // V == release delta = 1; rc = PyArg_ParseTupleAndKeywords(args, keywords, "|h", keyword_list[SEMOP_V], &delta); if (rc && !delta) { rc = 0; PyErr_SetString(PyExc_ValueError, "The delta must be non-zero"); } else delta = abs(delta); break; case SEMOP_Z: // Z = Zero test delta = 0; rc = PyArg_ParseTupleAndKeywords(args, keywords, "|O&", keyword_list[SEMOP_Z], convert_timeout, &timeout); break; default: PyErr_Format(pInternalException, "Bad op_type (%d)", op_type); rc = 0; break; } if (!rc) goto error_return; // Now that the caller's params have been vetted, I set up the op struct // that I'm going to pass to semop(). op[0].sem_num = 0; op[0].sem_op = delta; op[0].sem_flg = self->op_flags; Py_BEGIN_ALLOW_THREADS; #ifdef SEMTIMEDOP_EXISTS // Call semtimedop() if appropriate, otherwise call semop() if (!timeout.is_none) { DPRINTF("calling semtimedop on id %d, op.sem_op=%d, op.flags=0x%x\n", self->id, op[0].sem_op, op[0].sem_flg); DPRINTF("timeout tv_sec = %ld; timeout tv_nsec = %ld\n", timeout.timestamp.tv_sec, timeout.timestamp.tv_nsec); rc = semtimedop(self->id, op, 1, &timeout.timestamp); } else { DPRINTF("calling semop on id %d, op.sem_op = %d, op.flags=%x\n", self->id, op[0].sem_op, op[0].sem_flg); rc = semop(self->id, op, 1); } #else // no support for semtimedop(), always call semop() instead. DPRINTF("calling semop on id %d, op.sem_op = %d, op.flags=%x\n", self->id, op[0].sem_op, op[0].sem_flg); rc = semop(self->id, op, 1); #endif Py_END_ALLOW_THREADS; if (rc == -1) { sem_set_error(); goto error_return; } Py_RETURN_NONE; error_return: return NULL; } // cmd can be any of the values defined in the documentation for semctl(). static PyObject * sem_get_semctl_value(int semaphore_id, int cmd) { int rc; // semctl() returns an int // ref: http://www.opengroup.org/onlinepubs/000095399/functions/semctl.html rc = semctl(semaphore_id, 0, cmd); if (-1 == rc) { sem_set_error(); goto error_return; } #if PY_MAJOR_VERSION > 2 return PyLong_FromLong(rc); #else return PyInt_FromLong(rc); #endif error_return: return NULL; } static PyObject * sem_get_ipc_perm_value(int id, enum GET_SET_IDENTIFIERS field) { struct semid_ds sem_info; union semun arg; PyObject *py_value = NULL; arg.buf = &sem_info; // Here I get the values currently associated with the semaphore. if (-1 == semctl(id, 0, IPC_STAT, arg)) { sem_set_error(); goto error_return; } switch (field) { case SVIFP_IPC_PERM_UID: py_value = UID_T_TO_PY(sem_info.sem_perm.uid); break; case SVIFP_IPC_PERM_GID: py_value = GID_T_TO_PY(sem_info.sem_perm.gid); break; case SVIFP_IPC_PERM_CUID: py_value = UID_T_TO_PY(sem_info.sem_perm.cuid); break; case SVIFP_IPC_PERM_CGID: py_value = GID_T_TO_PY(sem_info.sem_perm.cgid); break; case SVIFP_IPC_PERM_MODE: py_value = MODE_T_TO_PY(sem_info.sem_perm.mode); break; // This isn't an ipc_perm value but it fits here anyway. case SVIFP_SEM_OTIME: py_value = TIME_T_TO_PY(sem_info.sem_otime); break; default: PyErr_Format(pInternalException, "Bad field %d passed to sem_get_ipc_perm_value", field); goto error_return; break; } return py_value; error_return: return NULL; } static int sem_set_ipc_perm_value(int id, enum GET_SET_IDENTIFIERS field, PyObject *py_value) { struct semid_ds sem_info; union semun arg; arg.buf = &sem_info; #if PY_MAJOR_VERSION > 2 if (!PyLong_Check(py_value)) #else if (!PyInt_Check(py_value)) #endif { PyErr_Format(PyExc_TypeError, "The attribute must be an integer"); goto error_return; } arg.buf = &sem_info; /* Here I get the current values associated with the semaphore. It's critical to populate sem_info with current values here (rather than just using the struct filled with whatever garbage it acquired from being declared on the stack) because the call to semctl(...IPC_SET...) below will copy uid, gid and mode to the kernel's data structure. */ if (-1 == semctl(id, 0, IPC_STAT, arg)) { sem_set_error(); goto error_return; } // Below I'm stuffing a Python int converted to a C long into a // uid_t, gid_t or mode_t. A long might not fit, hence the explicit // cast. If the user passes a value that's too big, tough cookies. switch (field) { case SVIFP_IPC_PERM_UID: #if PY_MAJOR_VERSION > 2 sem_info.sem_perm.uid = (uid_t)PyLong_AsLong(py_value); #else sem_info.sem_perm.uid = (uid_t)PyInt_AsLong(py_value); #endif break; case SVIFP_IPC_PERM_GID: #if PY_MAJOR_VERSION > 2 sem_info.sem_perm.gid = (gid_t)PyLong_AsLong(py_value); #else sem_info.sem_perm.gid = (gid_t)PyInt_AsLong(py_value); #endif break; case SVIFP_IPC_PERM_MODE: #if PY_MAJOR_VERSION > 2 sem_info.sem_perm.mode = (mode_t)PyLong_AsLong(py_value); #else sem_info.sem_perm.mode = (mode_t)PyInt_AsLong(py_value); #endif break; default: PyErr_Format(pInternalException, "Bad field %d passed to sem_set_ipc_perm_value", field); goto error_return; break; } if (-1 == semctl(id, 0, IPC_SET, arg)) { sem_set_error(); goto error_return; } return 0; error_return: return -1; } PyObject * sem_remove(int id) { if (NULL == sem_get_semctl_value(id, IPC_RMID)) return NULL; else Py_RETURN_NONE; } void Semaphore_dealloc(Semaphore *self) { Py_TYPE(self)->tp_free((PyObject*)self); } PyObject * Semaphore_new(PyTypeObject *type, PyObject *args, PyObject *keywords) { Semaphore *self; self = (Semaphore *)type->tp_alloc(type, 0); return (PyObject *)self; } int Semaphore_init(Semaphore *self, PyObject *args, PyObject *keywords) { int mode = 0600; int initial_value = 0; int flags = 0; union semun arg; char *keyword_list[ ] = {"key", "flags", "mode", "initial_value", NULL}; NoneableKey key; //Semaphore(key, [flags = 0, [mode = 0600, [initial_value = 0]]]) if (!PyArg_ParseTupleAndKeywords(args, keywords, "O&|iii", keyword_list, &convert_key_param, &key, &flags, &mode, &initial_value)) goto error_return; DPRINTF("key is none = %d, key value = %ld\n", key.is_none, (long)key.value); if ( !(flags & IPC_CREAT) && (flags & IPC_EXCL) ) { PyErr_SetString(PyExc_ValueError, "IPC_EXCL must be combined with IPC_CREAT"); goto error_return; } if (key.is_none && ((flags & IPC_EXCL) != IPC_EXCL)) { PyErr_SetString(PyExc_ValueError, "Key can only be None if IPC_EXCL is set"); goto error_return; } self->op_flags = 0; // I mask the caller's flags against the two IPC_* flags to ensure that // nothing funky sneaks into the flags. flags &= (IPC_CREAT | IPC_EXCL); // Note that Sys V sems can be in "sets" (arrays) but I hardcode this // to always be a set with just one member. // Permissions and flags (i.e. IPC_CREAT | IPC_EXCL) are both crammed // into the 3rd param. if (key.is_none) { // (key == None) ==> generate a key for the caller do { errno = 0; self->key = get_random_key(); DPRINTF("Calling semget, key=%ld, mode=%o, flags=%x\n", (long)self->key, mode, flags); self->id = semget(self->key, 1, mode | flags); } while ( (-1 == self->id) && (EEXIST == errno) ); } else { // (key != None) ==> use key supplied by the caller self->key = key.value; DPRINTF("Calling semget, key=%ld, mode=%o, flags=%x\n", (long)self->key, mode, flags); self->id = semget(self->key, 1, mode | flags); } DPRINTF("id == %d\n", self->id); if (self->id == -1) { sem_set_error(); goto error_return; } // Before attempting to set the initial value, I have to be sure that // I created this semaphore and that I have write access to it. if ((flags & IPC_CREX) && (mode & 0200)) { DPRINTF("setting initial value to %d\n", initial_value); arg.val = initial_value; if (-1 == semctl(self->id, 0, SETVAL, arg)) { sem_set_error(); goto error_return; } } return 0; error_return: return -1; } PyObject * Semaphore_P(Semaphore *self, PyObject *args, PyObject *keywords) { return sem_perform_semop(SEMOP_P, self, args, keywords); } PyObject * Semaphore_acquire(Semaphore *self, PyObject *args, PyObject *keywords) { return Semaphore_P(self, args, keywords); } PyObject * Semaphore_V(Semaphore *self, PyObject *args, PyObject *keywords) { return sem_perform_semop(SEMOP_V, self, args, keywords); } PyObject * Semaphore_release(Semaphore *self, PyObject *args, PyObject *keywords) { return Semaphore_V(self, args, keywords); } PyObject * Semaphore_Z(Semaphore *self, PyObject *args, PyObject *keywords) { return sem_perform_semop(SEMOP_Z, self, args, keywords); } PyObject * Semaphore_remove(Semaphore *self) { return sem_remove(self->id); } PyObject * Semaphore_enter(Semaphore *self) { PyObject *args = PyTuple_New(0); PyObject *retval = NULL; if (Semaphore_acquire(self, args, NULL)) { retval = (PyObject *)self; Py_INCREF(self); } Py_DECREF(args); return retval; } PyObject * Semaphore_exit(Semaphore *self, PyObject *args) { PyObject *release_args = PyTuple_New(0); PyObject *retval = NULL; DPRINTF("exiting context and releasing semaphore %s\n", self->key); retval = Semaphore_release(self, release_args, NULL); Py_DECREF(release_args); return retval; } PyObject * sem_get_key(Semaphore *self) { return KEY_T_TO_PY(self->key); } PyObject * sem_get_value(Semaphore *self) { return sem_get_semctl_value(self->id, GETVAL); } int sem_set_value(Semaphore *self, PyObject *py_value) { union semun arg; long value; #if PY_MAJOR_VERSION > 2 if (!PyLong_Check(py_value)) #else if (!PyInt_Check(py_value)) #endif { PyErr_Format(PyExc_TypeError, "Attribute 'value' must be an integer"); goto error_return; } #if PY_MAJOR_VERSION > 2 value = PyLong_AsLong(py_value); #else value = PyInt_AsLong(py_value); #endif DPRINTF("C value is %ld\n", value); if ((-1 == value) && PyErr_Occurred()) { // No idea wht could cause this -- just raise it to the caller. goto error_return; } if ((value < 0) || (value > SEMAPHORE_VALUE_MAX)) { PyErr_Format(PyExc_ValueError, "Attribute 'value' must be between 0 and %ld (SEMAPHORE_VALUE_MAX)", (long)SEMAPHORE_VALUE_MAX); goto error_return; } arg.val = value; if (-1 == semctl(self->id, 0, SETVAL, arg)) { sem_set_error(); goto error_return; } return 0; error_return: return -1; } PyObject * sem_get_block(Semaphore *self) { DPRINTF("op_flags: %x\n", self->op_flags); return PyBool_FromLong( (self->op_flags & IPC_NOWAIT) ? 0 : 1); } int sem_set_block(Semaphore *self, PyObject *py_value) { DPRINTF("op_flags before: %x\n", self->op_flags); if (PyObject_IsTrue(py_value)) self->op_flags &= ~IPC_NOWAIT; else self->op_flags |= IPC_NOWAIT; DPRINTF("op_flags after: %x\n", self->op_flags); return 0; } PyObject * sem_get_mode(Semaphore *self) { return sem_get_ipc_perm_value(self->id, SVIFP_IPC_PERM_MODE); } int sem_set_mode(Semaphore *self, PyObject *py_value) { return sem_set_ipc_perm_value(self->id, SVIFP_IPC_PERM_MODE, py_value); } PyObject * sem_get_undo(Semaphore *self) { return PyBool_FromLong( (self->op_flags & SEM_UNDO) ? 1 : 0 ); } int sem_set_undo(Semaphore *self, PyObject *py_value) { DPRINTF("op_flags before: %x\n", self->op_flags); if (PyObject_IsTrue(py_value)) self->op_flags |= SEM_UNDO; else self->op_flags &= ~SEM_UNDO; DPRINTF("op_flags after: %x\n", self->op_flags); return 0; } PyObject * sem_get_uid(Semaphore *self) { return sem_get_ipc_perm_value(self->id, SVIFP_IPC_PERM_UID); } int sem_set_uid(Semaphore *self, PyObject *py_value) { return sem_set_ipc_perm_value(self->id, SVIFP_IPC_PERM_UID, py_value); } PyObject * sem_get_gid(Semaphore *self) { return sem_get_ipc_perm_value(self->id, SVIFP_IPC_PERM_GID); } int sem_set_gid(Semaphore *self, PyObject *py_value) { return sem_set_ipc_perm_value(self->id, SVIFP_IPC_PERM_GID, py_value); } PyObject * sem_get_c_uid(Semaphore *self) { return sem_get_ipc_perm_value(self->id, SVIFP_IPC_PERM_CUID); } PyObject * sem_get_c_gid(Semaphore *self) { return sem_get_ipc_perm_value(self->id, SVIFP_IPC_PERM_CGID); } PyObject * sem_get_last_pid(Semaphore *self) { return sem_get_semctl_value(self->id, GETPID); } PyObject * sem_get_waiting_for_nonzero(Semaphore *self) { return sem_get_semctl_value(self->id, GETNCNT); } PyObject * sem_get_waiting_for_zero(Semaphore *self) { return sem_get_semctl_value(self->id, GETZCNT); } PyObject * sem_get_o_time(Semaphore *self) { return sem_get_ipc_perm_value(self->id, SVIFP_SEM_OTIME); } sysv_ipc-0.6.8/semaphore.h0000644000076500000240000000325412374502557015262 0ustar mestaff00000000000000typedef struct { PyObject_HEAD key_t key; int id; short op_flags; } Semaphore; /* Object methods */ PyObject *Semaphore_new(PyTypeObject *type, PyObject *, PyObject *); int Semaphore_init(Semaphore *, PyObject *, PyObject *); void Semaphore_dealloc(Semaphore *); PyObject *Semaphore_enter(Semaphore *); PyObject *Semaphore_exit(Semaphore *, PyObject *); PyObject *Semaphore_P(Semaphore *, PyObject *, PyObject *); PyObject *Semaphore_acquire(Semaphore *, PyObject *, PyObject *); PyObject *Semaphore_V(Semaphore *, PyObject *, PyObject *); PyObject *Semaphore_release(Semaphore *, PyObject *, PyObject *); PyObject *Semaphore_Z(Semaphore *, PyObject *, PyObject *); PyObject *Semaphore_remove(Semaphore *); /* Object attributes (read-write & read-only) */ PyObject *sem_get_value(Semaphore *); int sem_set_value(Semaphore *self, PyObject *py_value); PyObject *sem_get_block(Semaphore *); int sem_set_block(Semaphore *self, PyObject *py_value); PyObject *sem_get_mode(Semaphore *); int sem_set_mode(Semaphore *, PyObject *); PyObject *sem_get_undo(Semaphore *); int sem_set_undo(Semaphore *self, PyObject *py_value); PyObject *sem_get_uid(Semaphore *); int sem_set_uid(Semaphore *, PyObject *); PyObject *sem_get_gid(Semaphore *); int sem_set_gid(Semaphore *, PyObject *); PyObject *sem_get_key(Semaphore *); PyObject *sem_get_c_uid(Semaphore *); PyObject *sem_get_c_gid(Semaphore *); PyObject *sem_get_last_pid(Semaphore *); PyObject *sem_get_waiting_for_nonzero(Semaphore *); PyObject *sem_get_waiting_for_zero(Semaphore *); PyObject *sem_get_o_time(Semaphore *); PyObject *sem_str(Semaphore *); PyObject *sem_repr(Semaphore *); /* Utility functions */ PyObject *sem_remove(int); sysv_ipc-0.6.8/setup.py0000644000076500000240000000464712367273337014652 0ustar mestaff00000000000000# Python modules import distutils.core as duc # sysv_ipc installation helper module import prober VERSION = open("VERSION", "r").read().strip() name = "sysv_ipc" description = "System V IPC primitives (semaphores, shared memory and message queues) for Python" long_description = open("README", "r").read() author = "Philip Semanchuk" author_email = "philip@semanchuk.com" maintainer = "Philip Semanchuk" url = "http://semanchuk.com/philip/sysv_ipc/" download_url = "http://semanchuk.com/philip/sysv_ipc/sysv_ipc-%s.tar.gz" % VERSION source_files = ["sysv_ipc_module.c", "common.c", "semaphore.c", "memory.c", "mq.c" ] # http://pypi.python.org/pypi?:action=list_classifiers classifiers = [ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "License :: OSI Approved :: BSD License", "Operating System :: MacOS :: MacOS X", "Operating System :: POSIX :: BSD :: FreeBSD", "Operating System :: POSIX :: Linux", "Operating System :: POSIX :: SunOS/Solaris", "Operating System :: POSIX", "Operating System :: Unix", "Programming Language :: Python", "Programming Language :: Python :: 2", "Programming Language :: Python :: 3", "Topic :: Utilities" ] license = "http://creativecommons.org/licenses/BSD/" keywords = "ipc inter-process communication semaphore shared memory shm message queue" prober.probe() extension = duc.Extension("sysv_ipc", source_files, # extra_compile_args=['-E'] depends = [ "common.c", "common.h", "memory.c", "memory.h", "mq.c", "mq.h", "probe_results.h", "semaphore.c", "semaphore.h", "sysv_ipc_module.c", ], ) duc.setup(name = name, version = VERSION, description = description, long_description = long_description, author = author, author_email = author_email, maintainer = maintainer, url = url, download_url = download_url, classifiers = classifiers, license = license, keywords = keywords, ext_modules = [ extension ] ) sysv_ipc-0.6.8/sysv_ipc_module.c0000644000076500000240000006633412367005000016465 0ustar mestaff00000000000000/* sysv_ipc - A Python module for accessing System V semaphores, shared memory and message queues. Copyright (c) 2008, Philip Semanchuk All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of sysv_ipc nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY Philip Semanchuk ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Philip Semanchuk BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "Python.h" #include "structmember.h" // For memset #include // For srand #include #include // For the math surrounding timeouts for semtimedop() #include #include "common.h" #include "semaphore.h" #include "memory.h" #include "mq.h" PyObject *pBaseException; PyObject *pInternalException; PyObject *pPermissionsException; PyObject *pExistentialException; PyObject *pBusyException; PyObject *pNotAttachedException; // sysv_ipc_attach() needs this forward declaration of SharedMemoryType static PyTypeObject SharedMemoryType; /* Module methods */ static PyObject * sysv_ipc_attach(PyObject *self, PyObject *args, PyObject *keywords) { // Given the id of an extant shared memory segment and (optionally) an // address and shmat flags, attempts to attach the memory. If successful, // returns a new SharedMemory object with the key permanently set to -1. SharedMemory *shm = NULL; PyObject *py_address = NULL; int id = -1; void *address = NULL; int flags = 0; char *keyword_list[ ] = {"id", "address", "flags", NULL}; if (!PyArg_ParseTupleAndKeywords(args, keywords, "i|Oi", keyword_list, &id, &py_address, &flags)) goto error_return; if ((!py_address) || (py_address == Py_None)) address = NULL; else { if (PyLong_Check(py_address)) address = PyLong_AsVoidPtr(py_address); else { PyErr_SetString(PyExc_TypeError, "address must be a long"); goto error_return; } } DPRINTF("About to create a new SharedMemory object.\n"); /* Create a new SharedMemory object. Some tutorials recommend using PyObject_CallObject() to create this, but that invokes the __init__ method which I don't want to do. */ shm = (SharedMemory *)PyObject_New(SharedMemory, &SharedMemoryType); shm->id = id; shm->address = address; DPRINTF("About to call shm_attach()\n"); if (Py_None == shm_attach(shm, flags)) // All is well return (PyObject *)shm; else // abandon this object and fall through to the error return below. Py_DECREF(shm); error_return: return NULL; } static PyObject * sysv_ipc_ftok(PyObject *self, PyObject *args, PyObject *keywords) { char *path; int id = 0; int silence_warning = 0; char *keyword_list[ ] = {"path", "id", "silence_warning", NULL}; key_t rc = 0; if (!PyArg_ParseTupleAndKeywords(args, keywords, "si|i", keyword_list, &path, &id, &silence_warning)) goto error_return; if (!silence_warning) { DPRINTF("path=%s, id=%d, rc=%ld\n", path, id, rc); PyErr_WarnEx(PyExc_Warning, "Use of ftok() is not recommended; see sysv_ipc documentation", 1); } rc = ftok(path, id); DPRINTF("path=%s, id=%d, rc=%ld\n", path, id, rc); return Py_BuildValue("i", rc); error_return: return NULL; } static PyObject * sysv_ipc_remove_semaphore(PyObject *self, PyObject *args) { int id; if (!PyArg_ParseTuple(args, "i", &id)) goto error_return; DPRINTF("removing sem with id %d\n", id); if (NULL == sem_remove(id)) goto error_return; Py_RETURN_NONE; error_return: return NULL; } static PyObject * sysv_ipc_remove_shared_memory(PyObject *self, PyObject *args) { int id; if (!PyArg_ParseTuple(args, "i", &id)) goto error_return; return shm_remove(id); error_return: return NULL; } static PyObject * sysv_ipc_remove_message_queue(PyObject *self, PyObject *args) { int id; if (!PyArg_ParseTuple(args, "i", &id)) goto error_return; return mq_remove(id); error_return: return NULL; } static PyMemberDef Semaphore_members[] = { {"id", T_INT, offsetof(Semaphore, id), READONLY, "The id assigned by the system"}, {NULL} /* Sentinel */ }; static PyMethodDef Semaphore_methods[] = { { "__enter__", (PyCFunction)Semaphore_enter, METH_NOARGS, }, { "__exit__", (PyCFunction)Semaphore_exit, METH_VARARGS, }, { "P", (PyCFunction)Semaphore_P, METH_VARARGS | METH_KEYWORDS, "Acquire (decrement) the semaphore, waiting if necessary" }, { "acquire", (PyCFunction)Semaphore_acquire, METH_VARARGS | METH_KEYWORDS, "Acquire (decrement) the semaphore, waiting if necessary" }, { "V", (PyCFunction)Semaphore_V, METH_VARARGS | METH_KEYWORDS, "Release (increment) the semaphore" }, { "release", (PyCFunction)Semaphore_release, METH_VARARGS | METH_KEYWORDS, "Release (increment) the semaphore" }, { "Z", (PyCFunction)Semaphore_Z, METH_VARARGS | METH_KEYWORDS, "Waits until zee zemaphore is zero" }, { "remove", (PyCFunction)Semaphore_remove, METH_NOARGS, "Removes (deletes) the semaphore from the system" }, {NULL, NULL, 0, NULL} /* Sentinel */ }; static PyGetSetDef Semaphore_gets_and_sets[] = { { "key", (getter)sem_get_key, (setter)NULL, "The key passed to the constructor", NULL }, { "value", (getter)sem_get_value, (setter)sem_set_value, "The semaphore's current value", NULL }, { "undo", (getter)sem_get_undo, (setter)sem_set_undo, "When True, acquire/release operations will be undone when the process exits. Non-portable.", NULL }, { "block", (getter)sem_get_block, (setter)sem_set_block, "When True (the default), calls to acquire/release/P/V/Z will wait (block) if the semaphore is busy", NULL }, { "mode", (getter)sem_get_mode, (setter)sem_set_mode, "Permissions", NULL }, { "uid", (getter)sem_get_uid, (setter)sem_set_uid, "The semaphore's UID", NULL }, { "gid", (getter)sem_get_gid, (setter)sem_set_gid, "The semaphore's GID", NULL }, { "cuid", (getter)sem_get_c_uid, (setter)NULL, "The semaphore creator's UID. Read only.", NULL }, { "cgid", (getter)sem_get_c_gid, (setter)NULL, "The semaphore creator's GID. Read only.", NULL }, { "last_pid", (getter)sem_get_last_pid, (setter)NULL, "The id of the last process to call acquire()/release()/Z() on this semaphore. Read only.", NULL }, { "waiting_for_nonzero", (getter)sem_get_waiting_for_nonzero, (setter)NULL, "The number of processes waiting for the semaphore to become non-zero. Read only.", NULL }, { "waiting_for_zero", (getter)sem_get_waiting_for_zero, (setter)NULL, "The number of processes waiting for the semaphore to become zero. Read only.", NULL }, { "o_time", (getter)sem_get_o_time, (setter)NULL, "The last time semop (acquire/release/P/V/Z) was called on this semaphore. Initialized to zero. Read only.", NULL }, {NULL} /* Sentinel */ }; static PyTypeObject SemaphoreType = { PyVarObject_HEAD_INIT(NULL, 0) "sysv_ipc.Semaphore", // tp_name sizeof(Semaphore), // tp_basicsize 0, // tp_itemsize (destructor)Semaphore_dealloc, // tp_dealloc 0, // tp_print 0, // tp_getattr 0, // tp_setattr 0, // tp_compare (reprfunc)sem_repr, // tp_repr 0, // tp_as_number 0, // tp_as_sequence 0, // tp_as_mapping 0, // tp_hash 0, // tp_call (reprfunc)sem_str, // tp_str 0, // tp_getattro 0, // tp_setattro 0, // tp_as_buffer Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, // tp_flags "System V semaphore object", // tp_doc 0, // tp_traverse 0, // tp_clear 0, // tp_richcompare 0, // tp_weaklistoffset 0, // tp_iter 0, // tp_iternext Semaphore_methods, // tp_methods Semaphore_members, // tp_members Semaphore_gets_and_sets, // tp_getset 0, // tp_base 0, // tp_dict 0, // tp_descr_get 0, // tp_descr_set 0, // tp_dictoffset (initproc)Semaphore_init, // tp_init 0, // tp_alloc Semaphore_new, // tp_new }; static PyMemberDef SharedMemory_members[] = { {"id", T_INT, offsetof(SharedMemory, id), READONLY, "The id assigned by the system"}, {NULL} /* Sentinel */ }; static PyMethodDef SharedMemory_methods[] = { { "read", (PyCFunction)SharedMemory_read, METH_VARARGS | METH_KEYWORDS, "Read n bytes from the shared memory at the given offset into a Python string" }, { "write", (PyCFunction)SharedMemory_write, METH_VARARGS | METH_KEYWORDS, "Write the string to the shared memory at the offset given" }, { "remove", (PyCFunction)SharedMemory_remove, METH_NOARGS, "Removes (deletes) the shared memory from the system" }, { "attach", (PyCFunction)SharedMemory_attach, METH_VARARGS, "Attaches the shared memory" }, { "detach", (PyCFunction)SharedMemory_detach, METH_NOARGS, "Detaches the shared memory" }, {NULL, NULL, 0, NULL} /* Sentinel */ }; static PyGetSetDef SharedMemory_gets_and_sets[] = { { "key", (getter)shm_get_key, (setter)NULL, "The key passed to the constructor. Read only.", NULL }, { "size", (getter)shm_get_size, (setter)NULL, "The size of the segment in bytes. Read only.", NULL }, { "address", (getter)shm_get_address, (setter)NULL, "The memory address of the segment. Read only.", NULL }, { "attached", (getter)shm_get_attached, (setter)NULL, "True if the segment is attached. Read only.", NULL }, { "last_attach_time", (getter)shm_get_last_attach_time, (setter)NULL, "The most recent time this segment was attached. Read only.", NULL }, { "last_detach_time", (getter)shm_get_last_detach_time, (setter)NULL, "The most recent time this segment was detached. Read only.", NULL }, { "last_change_time", (getter)shm_get_last_change_time, (setter)NULL, "The time of the most recent change to this segment's uid, gid, mode, or the time the segment was removed. Read only.", NULL }, { "creator_pid", (getter)shm_get_creator_pid, (setter)NULL, "The process id of the creator. Read only.", NULL }, { "last_pid", (getter)shm_get_last_pid, (setter)NULL, "The id of the process that performed the most recent attach or detach. Read only.", NULL }, { "number_attached", (getter)shm_get_number_attached, (setter)NULL, "The current number of attached processes. Read only.", NULL }, { "uid", (getter)shm_get_uid, (setter)shm_set_uid, "The segment's UID.", NULL }, { "gid", (getter)shm_get_gid, (setter)shm_set_gid, "The segment's GID.", NULL }, { "cuid", (getter)shm_get_cuid, (setter)NULL, "The UID of the segment's creator. Read only.", NULL }, { "cgid", (getter)shm_get_cgid, (setter)NULL, "The GID of the segment's creator. Read only.", NULL }, { "mode", (getter)shm_get_mode, (setter)shm_set_mode, "Permissions.", NULL }, {NULL} /* Sentinel */ }; static PyTypeObject SharedMemoryType = { PyVarObject_HEAD_INIT(NULL, 0) "sysv_ipc.SharedMemory", // tp_name sizeof(SharedMemory), // tp_basicsize 0, // tp_itemsize (destructor)SharedMemory_dealloc, // tp_dealloc 0, // tp_print 0, // tp_getattr 0, // tp_setattr 0, // tp_compare (reprfunc)shm_repr, // tp_repr 0, // tp_as_number 0, // tp_as_sequence 0, // tp_as_mapping 0, // tp_hash 0, // tp_call (reprfunc)shm_str, // tp_str 0, // tp_getattro 0, // tp_setattro 0, // tp_as_buffer Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, // tp_flags "System V shared memory object", // tp_doc 0, // tp_traverse 0, // tp_clear 0, // tp_richcompare 0, // tp_weaklistoffset 0, // tp_iter 0, // tp_iternext SharedMemory_methods, // tp_methods SharedMemory_members, // tp_members SharedMemory_gets_and_sets, // tp_getset 0, // tp_base 0, // tp_dict 0, // tp_descr_get 0, // tp_descr_set 0, // tp_dictoffset (initproc)SharedMemory_init, // tp_init 0, // tp_alloc SharedMemory_new, // tp_new }; static PyMemberDef MessageQueue_members[] = { {"id", T_INT, offsetof(MessageQueue, id), READONLY, "Message queue id"}, {NULL} /* Sentinel */ }; static PyMethodDef MessageQueue_methods[] = { { "send", (PyCFunction)MessageQueue_send, METH_VARARGS | METH_KEYWORDS, "Place a message on the queue" }, { "receive", (PyCFunction)MessageQueue_receive, METH_VARARGS | METH_KEYWORDS, "Receive a message from the queue" }, { "remove", (PyCFunction)MessageQueue_remove, METH_NOARGS, "Removes (deletes) the queue from the system" }, {NULL, NULL, 0, NULL} /* Sentinel */ }; static PyGetSetDef MessageQueue_gets_and_sets[] = { { "key", (getter)mq_get_key, (setter)NULL, "The key passed to the constructor.", NULL }, { "last_send_time", (getter)mq_get_last_send_time, (setter)NULL, "A Unix timestamp representing the last time a message was sent.", NULL }, { "last_receive_time", (getter)mq_get_last_receive_time, (setter)NULL, "A Unix timestamp representing the last time a message was received.", NULL }, { "last_change_time", (getter)mq_get_last_change_time, (setter)NULL, "A Unix timestamp representing the last time the queue was changed.", NULL }, { "current_messages", (getter)mq_get_current_messages, (setter)NULL, "The number of messages currently in the queue", NULL }, { "last_send_pid", (getter)mq_get_last_send_pid, (setter)NULL, "The id of the last process which sent via the queue", NULL}, { "last_receive_pid", (getter)mq_get_last_receive_pid, (setter)NULL, "The id of the last process which received from the queue", NULL }, { "max_size", (getter)mq_get_max_size, (setter)mq_set_max_size, "The maximum size of the queue (in bytes). Read-write if you have sufficient privileges.", NULL }, { "mode", (getter)mq_get_mode, (setter)mq_set_mode, "Permissions", NULL }, { "uid", (getter)mq_get_uid, (setter)mq_set_uid, "The queue's UID.", NULL }, { "gid", (getter)mq_get_gid, (setter)mq_set_gid, "The queue's GID.", NULL }, { "cuid", (getter)mq_get_c_uid, (setter)NULL, "The UID of the queue's creator. Read only.", NULL }, { "cgid", (getter)mq_get_c_gid, (setter)NULL, "The GID of the queue's creator. Read only.", NULL }, {NULL} /* Sentinel */ }; static PyTypeObject MessageQueueType = { PyVarObject_HEAD_INIT(NULL, 0) "sysv_ipc.MessageQueue", // tp_name sizeof(MessageQueue), // tp_basicsize 0, // tp_itemsize (destructor)MessageQueue_dealloc, // tp_dealloc 0, // tp_print 0, // tp_getattr 0, // tp_setattr 0, // tp_compare (reprfunc)mq_repr, // tp_repr 0, // tp_as_number 0, // tp_as_sequence 0, // tp_as_mapping 0, // tp_hash 0, // tp_call (reprfunc)mq_str, // tp_str 0, // tp_getattro 0, // tp_setattro 0, // tp_as_buffer Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, // tp_flags "System V message queue object", // tp_doc 0, // tp_traverse 0, // tp_clear 0, // tp_richcompare 0, // tp_weaklistoffset 0, // tp_iter 0, // tp_iternext MessageQueue_methods, // tp_methods MessageQueue_members, // tp_members MessageQueue_gets_and_sets, // tp_getset 0, // tp_base 0, // tp_dict 0, // tp_descr_get 0, // tp_descr_set 0, // tp_dictoffset (initproc)MessageQueue_init, // tp_init 0, // tp_alloc MessageQueue_new, // tp_new }; /* Module level stuff */ static PyMethodDef module_methods[ ] = { { "attach", (PyCFunction)sysv_ipc_attach, METH_VARARGS | METH_KEYWORDS, "Attaches the memory identified by the id and returns a new SharedMemory object." }, { "ftok", (PyCFunction)sysv_ipc_ftok, METH_VARARGS | METH_KEYWORDS, "Calls ftok(). Not recommended; see sysv_ipc documentation." }, { "remove_semaphore", (PyCFunction)sysv_ipc_remove_semaphore, METH_VARARGS, "Remove (delete) the semaphore identified by id" }, { "remove_shared_memory", (PyCFunction)sysv_ipc_remove_shared_memory, METH_VARARGS, "Remove shared memory identified by id" }, { "remove_message_queue", (PyCFunction)sysv_ipc_remove_message_queue, METH_VARARGS, "Remove the message queue identified by id" }, {NULL} /* Sentinel */ }; #if PY_MAJOR_VERSION > 2 static struct PyModuleDef this_module = { PyModuleDef_HEAD_INIT, // m_base "sysv_ipc", // m_name "SYSV IPC module", // m_doc -1, // m_size (space allocated for module globals) module_methods, // m_methods NULL, // m_reload NULL, // m_traverse NULL, // m_clear NULL // m_free }; #endif /* Module init function */ #if PY_MAJOR_VERSION > 2 #define SYSV_IPC_INIT_FUNCTION_NAME PyInit_sysv_ipc #else #define SYSV_IPC_INIT_FUNCTION_NAME initsysv_ipc #endif /* Module init function */ PyMODINIT_FUNC SYSV_IPC_INIT_FUNCTION_NAME(void) { PyObject *module; PyObject *module_dict; // I seed the random number generator in case I'm asked to make some // random keys. srand((unsigned int)time(NULL)); #if PY_MAJOR_VERSION > 2 module = PyModule_Create(&this_module); #else module = Py_InitModule3("sysv_ipc", module_methods, "System V IPC module"); #endif if (!module) goto error_return; if (PyType_Ready(&SemaphoreType) < 0) goto error_return; if (PyType_Ready(&SharedMemoryType) < 0) goto error_return; if (PyType_Ready(&MessageQueueType) < 0) goto error_return; #ifdef SEMTIMEDOP_EXISTS Py_INCREF(Py_True); PyModule_AddObject(module, "SEMAPHORE_TIMEOUT_SUPPORTED", Py_True); #else Py_INCREF(Py_False); PyModule_AddObject(module, "SEMAPHORE_TIMEOUT_SUPPORTED", Py_False); #endif PyModule_AddStringConstant(module, "VERSION", SYSV_IPC_VERSION); PyModule_AddStringConstant(module, "__version__", SYSV_IPC_VERSION); PyModule_AddStringConstant(module, "__copyright__", "Copyright 2008 - 2014 Philip Semanchuk"); PyModule_AddStringConstant(module, "__author__", "Philip Semanchuk"); PyModule_AddStringConstant(module, "__license__", "BSD"); PyModule_AddIntConstant(module, "PAGE_SIZE", PAGE_SIZE); PyModule_AddIntConstant(module, "KEY_MIN", KEY_MIN); PyModule_AddIntConstant(module, "KEY_MAX", KEY_MAX); PyModule_AddIntConstant(module, "SEMAPHORE_VALUE_MAX", SEMAPHORE_VALUE_MAX); PyModule_AddIntConstant(module, "IPC_CREAT", IPC_CREAT); PyModule_AddIntConstant(module, "IPC_EXCL", IPC_EXCL); PyModule_AddIntConstant(module, "IPC_CREX", IPC_CREX); PyModule_AddIntConstant(module, "IPC_PRIVATE", IPC_PRIVATE); PyModule_AddIntConstant(module, "SHM_RND", SHM_RND); PyModule_AddIntConstant(module, "SHM_RDONLY", SHM_RDONLY); // These flags are Linux-specific. #ifdef SHM_HUGETLB PyModule_AddIntConstant(module, "SHM_HUGETLB", SHM_HUGETLB); #endif #ifdef SHM_NORESERVE PyModule_AddIntConstant(module, "SHM_NORESERVE", SHM_NORESERVE); #endif #ifdef SHM_REMAP PyModule_AddIntConstant(module, "SHM_REMAP", SHM_REMAP); #endif Py_INCREF(&SemaphoreType); PyModule_AddObject(module, "Semaphore", (PyObject *)&SemaphoreType); Py_INCREF(&SharedMemoryType); PyModule_AddObject(module, "SharedMemory", (PyObject *)&SharedMemoryType); Py_INCREF(&MessageQueueType); PyModule_AddObject(module, "MessageQueue", (PyObject *)&MessageQueueType); // Exceptions if (!(module_dict = PyModule_GetDict(module))) goto error_return; if (!(pBaseException = PyErr_NewException("sysv_ipc.Error", NULL, NULL))) goto error_return; else PyDict_SetItemString(module_dict, "Error", pBaseException); if (!(pInternalException = PyErr_NewException("sysv_ipc.InternalError", NULL, NULL))) goto error_return; else PyDict_SetItemString(module_dict, "InternalError", pInternalException); if (!(pPermissionsException = PyErr_NewException("sysv_ipc.PermissionsError", pBaseException, NULL))) goto error_return; else PyDict_SetItemString(module_dict, "PermissionsError", pPermissionsException); if (!(pExistentialException = PyErr_NewException("sysv_ipc.ExistentialError", pBaseException, NULL))) goto error_return; else PyDict_SetItemString(module_dict, "ExistentialError", pExistentialException); if (!(pBusyException = PyErr_NewException("sysv_ipc.BusyError", pBaseException, NULL))) goto error_return; else PyDict_SetItemString(module_dict, "BusyError", pBusyException); if (!(pNotAttachedException = PyErr_NewException("sysv_ipc.NotAttachedError", pBaseException, NULL))) goto error_return; else PyDict_SetItemString(module_dict, "NotAttachedError", pNotAttachedException); #if PY_MAJOR_VERSION > 2 return module; #endif error_return: #if PY_MAJOR_VERSION > 2 return NULL; #else ; // Nothing to do #endif } sysv_ipc-0.6.8/VERSION0000644000076500000240000000001012403441662014151 0ustar mestaff000000000000000.6.8