From 53f4e6ccd74d217409ec38b506d7e7e4aeb7d738 Mon Sep 17 00:00:00 2001 From: Necoro <> Date: Fri, 18 Jan 2008 01:36:13 +0000 Subject: r643@Devoty: necoro | 2008-01-16 18:55:49 +0100 Fixed small bug in PackageTable.cb_package_revert_clicked r646@Devoty: necoro | 2008-01-18 00:12:30 +0100 Make tabpositions being configurable by the user r647@Devoty: necoro | 2008-01-18 01:38:19 +0100 Renamed 'shm' to '_shm' to not hide the global one r648@Devoty: necoro | 2008-01-18 01:38:29 +0100 Renamed 'shm' to '_shm' to not hide the global one r649@Devoty: necoro | 2008-01-18 02:34:43 +0100 Added update queues; general UI improvement --- _shm/__init__.py | 0 _shm/shm_wrapper.py | 301 ++++++++ _shm/shmmodule.c | 1396 +++++++++++++++++++++++++++++++++++ doc/TODO | 3 + etc/portato.cfg | 9 + portato.py | 2 +- portato/gui/gtk/dialogs.py | 6 + portato/gui/gtk/windows.py | 153 ++-- portato/gui/gtk/wrapper.py | 38 +- portato/gui/gui_helper.py | 66 +- portato/gui/templates/portato.glade | 455 +++++++----- portato/gui/wrapper.py | 34 + portato/plistener.py | 2 +- setup.py | 6 +- shm/__init__.py | 0 shm/shm_wrapper.py | 301 -------- shm/shmmodule.c | 1396 ----------------------------------- 17 files changed, 2222 insertions(+), 1946 deletions(-) create mode 100644 _shm/__init__.py create mode 100644 _shm/shm_wrapper.py create mode 100644 _shm/shmmodule.c delete mode 100644 shm/__init__.py delete mode 100644 shm/shm_wrapper.py delete mode 100644 shm/shmmodule.c diff --git a/_shm/__init__.py b/_shm/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/_shm/shm_wrapper.py b/_shm/shm_wrapper.py new file mode 100644 index 0000000..bf29f65 --- /dev/null +++ b/_shm/shm_wrapper.py @@ -0,0 +1,301 @@ +# shm_wrapper - A wrapper for the shm module which provides access +# to System V shared memory and semaphores on *nix systems. +# +# Copyright (c) 2007 by Philip Semanchuk +# Contact info at http://NikitaTheSpider.com/ +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +# Python modules +import random +import sys + +# Third party modules +import shm + +r"""shm_wrapper - A wrapper for the shm module which provides access +to System V shared memory and semaphores on *nix systems. + +The module shm is a Python wrapper around system functions like shmget. This +module in turn offers higher-level, more Pythonic access to shared memory and +semaphores. + +Full documentation is online at http://NikitaTheSpider.com/python/shm/ + +""" + +def create_memory(size, permissions = 0666, InitCharacter = ' '): + """ Creates a new shared memory segment. One can destroy it either by calling the + module-level method remove_memory() or by calling the .remove() method of a handle to + said memory. + """ + memory = None + + # I create the memory using a randomly-generated key. I keep trying until I find one + # that works or until I hit an error. + while not memory: + key = random.randint(1, sys.maxint - 1) + try: + memory = shm.create_memory(key, size, permissions) + except shm.error, ExtraData: + if shm.memory_haskey(key): + # Oops, bad luck, the key exists. I'll try another. I can't call + # memory_haskey() before calling create_memory() because that would create + # a race condition where I could verify a key is not used but then another + # process could call create_memory() with that key before I got a chance to + # do so. + pass + else: + # Uh-oh, something fundamental is wrong. + raise shm.error, ExtraData + + # Here I implicitly discard the memory handle object returned to me by shm and instead + # return my own handle to the shared memory segment. + memory = SharedMemoryHandle(key) + + memory.write(InitCharacter[0] * memory.size) + + return memory + + +def remove_memory(key): + # Destroys the shared memory segment. Raises KeyError if the key doesn't exist. + shm.remove_memory(shm.getshmid(key)) + + +class SharedMemoryHandle(object): + def __init__(self, key): + self._MemoryHandle = None + + # getshmid will raise a KeyError if there's no memory segment with this key. + shmid = shm.getshmid(key) + self._MemoryHandle = shm.memory(shmid) + + + def __del__(self): + if self._MemoryHandle: + # This will raise an error if the memory has been destroyed. + try: + if self._MemoryHandle.attached: + self._MemoryHandle.detach() + except shm.error: + pass + + + def remove(self): + if self._MemoryHandle: + if self._MemoryHandle.attached: + self._MemoryHandle.detach() + + shm.remove_memory(self._MemoryHandle.shmid) + self._MemoryHandle = None + + + def read(self, NumberOfBytes = 0, offset = 0): + if not self._MemoryHandle.attached: + self._MemoryHandle.attach() + + if not NumberOfBytes: + NumberOfBytes = self._MemoryHandle.size - offset + + return self._MemoryHandle.read(NumberOfBytes, offset) + + + def write(self, s, offset = 0): + if not self._MemoryHandle.attached: + self._MemoryHandle.attach() + + self._MemoryHandle.write(s, offset) + + + # Properties start here ================================================================ + + # key + def __get_key(self): return self._MemoryHandle.key + def __set_key(self, foo): raise AttributeError + key = property(__get_key, __set_key) + + # size of segment + def __get_size(self): return self._MemoryHandle.size + def __set_size(self, foo): raise AttributeError + size = property(__get_size, __set_size) + + # permissions + def __get_permissions(self): return self._MemoryHandle.perm + def __set_permissions(self, permissions): self._MemoryHandle.setperm(permissions) + permissions = property(__get_permissions, __set_permissions) + + # The number of processes currently attached to this memory segment. + def __get_number_attached(self): return self._MemoryHandle.nattch + def __set_number_attached(self, foo): raise AttributeError + number_attached = property(__get_number_attached, __set_number_attached) + + # segment's uid + def __get_uid(self): return self._MemoryHandle.uid + def __set_uid(self, uid): self._MemoryHandle.setuid(uid) + uid = property(__get_uid, __set_uid) + + # segment's gid + def __get_gid(self): return self._MemoryHandle.gid + def __set_gid(self, gid): self._MemoryHandle.setgid(gid) + gid = property(__get_gid, __set_gid) + + # Creator uid (read-only) + def __get_creator_uid(self): return self._MemoryHandle.cuid + def __set_creator_uid(self, foo): raise AttributeError + creator_uid = property(__get_creator_uid, __set_creator_uid) + + # Creator gid (read-only) + def __get_creator_gid(self): return self._MemoryHandle.cgid + def __set_creator_gid(self, foo): raise AttributeError + creator_gid = property(__get_creator_gid, __set_creator_gid) + + # Creator pid (read-only) + def __get_creator_pid(self): return self._MemoryHandle.cpid + def __set_creator_pid(self, foo): raise AttributeError + creator_pid = property(__get_creator_pid, __set_creator_pid) + + # pid of last process to operate on this segment (read-only) + def __get_last_pid(self): return self._MemoryHandle.lpid + def __set_last_pid(self, foo): raise AttributeError + last_pid = property(__get_last_pid, __set_last_pid) + + + +def create_semaphore(InitialValue = 1, permissions = 0666): + """ Creates a new semaphore. One can destroy it either by calling the + module-level method remove_semaphore() or by calling the .remove() method of a + handle to said semaphore. + """ + semaphore = None + + # I create the semaphore using a randomly-generated key. I keep trying until I find one + # that works or until I hit an error. + while not semaphore: + key = random.randint(1, sys.maxint - 1) + try: + semaphore = shm.create_semaphore(key, InitialValue, permissions) + except shm.error, ExtraData: + if shm.semaphore_haskey(key): + # Oops, bad luck, the key exists. I'll try another. I can't call + # memory_haskey() before calling create_semaphore() because that would create + # a race condition where I could verify a key is not used but then another + # process could call create_semaphore() with that key before I got a chance to + # do so. + pass + else: + # Uh-oh, something fundamental is wrong. + raise ExtraData + + # Here I implicitly discard the semaphore object returned to me by shm and instead + # return my own handle to the semaphore. + return SemaphoreHandle(key) + + +def remove_semaphore(key): + # Destroys the semaphore. Raises KeyError if the key doesn't exist. + shm.remove_semaphore(shm.getsemid(key)) + + +class SemaphoreHandle(object): + def __init__(self, key): + # getsemid will raise a KeyError if appropriate. + self._SemaphoreHandle = shm.semaphore(shm.getsemid(key)) + + + def remove(self): + shm.remove_semaphore(self._SemaphoreHandle.semid) + self._SemaphoreHandle = None + + + def P(self): + # P = prolaag = probeer te verlagen (try to decrease) + self._SemaphoreHandle.P() + + + def V(self): + # V = verhoog (increase) + self._SemaphoreHandle.V() + + + def Z(self): + # Z = block until Zee semaphore is Zero + self._SemaphoreHandle.Z() + + + # Properties start here ================================================================ + def __get_key(self): return self._SemaphoreHandle.key + def __set_key(self, foo): raise AttributeError + key = property(__get_key, __set_key) + + + def __get_value(self): return self._SemaphoreHandle.val + def __set_value(self, value): self._semaphore.setval(value) + value = property(__get_value, __set_value) + + + def __get_WaitingForZero(self): return self._SemaphoreHandle.zcnt + def __set_WaitingForZero(self, foo): raise AttributeError + WaitingForZero = property(__get_WaitingForZero, __set_WaitingForZero) + + + def __get_WaitingForNonZero(self): return self._SemaphoreHandle.ncnt + def __set_WaitingForNonZero(self, foo): raise AttributeError + WaitingForNonZero = property(__get_WaitingForNonZero, __set_WaitingForNonZero) + + + def __get_blocking(self): return self._SemaphoreHandle.blocking + def __set_blocking(self, block): self._SemaphoreHandle.setblocking(block) + blocking = property(__get_blocking, __set_blocking) + + + def __get_undo(self): raise AttributeError + def __set_undo(self, undo): self._SemaphoreHandle.setundo(undo) + undo = property(__get_undo, __set_undo) + + + # segment's uid + def __get_uid(self): return self._SemaphoreHandle.uid + def __set_uid(self, uid): self._SemaphoreHandle.setuid(uid) + uid = property(__get_uid, __set_uid) + + + # segment's gid + def __get_gid(self): return self._SemaphoreHandle.gid + def __set_gid(self, gid): self._SemaphoreHandle.setgid(gid) + gid = property(__get_gid, __set_gid) + + + # Creator uid (read-only) + def __get_creator_uid(self): return self._SemaphoreHandle.cuid + def __set_creator_uid(self, foo): raise AttributeError + creator_uid = property(__get_creator_uid, __set_creator_uid) + + + # Creator gid (read-only) + def __get_creator_gid(self): return self._SemaphoreHandle.cgid + def __set_creator_gid(self, foo): raise AttributeError + creator_gid = property(__get_creator_gid, __set_creator_gid) + + + # Creator pid -- since semaphores have a lot of the same properties as memory + # objects, one would expect creator PID to be exposed here, but it isn't + # made available by the system (true AFAICT for BSDs, OS X and Solaris). + + + # pid of last process to operate on this segment (read-only) + def __get_last_pid(self): return self._SemaphoreHandle.lpid + def __set_last_pid(self, foo): raise AttributeError + last_pid = property(__get_last_pid, __set_last_pid) diff --git a/_shm/shmmodule.c b/_shm/shmmodule.c new file mode 100644 index 0000000..98596f1 --- /dev/null +++ b/_shm/shmmodule.c @@ -0,0 +1,1396 @@ +/**************************************************************************** + * + * shmmodule.c Copyright 1997, 1998 by INRIA. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY WARRANTIES, EXPRESS OR IMPLIED, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * + * IN NO EVENT SHALL THE INRIA OR THE AUTHORS 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. + * + ***************************************************************************/ + +/* Python Shared Memory module + Written by Vladimir Marangozov + Adopted by Philip Semanchuk + + This module provides an interface to System V shared memory IPC. + + Version history: + - 1.0 (sometime in the 1990s) - Released by Mr. Marangozov + - 1.1 (Feb 2007) - Released by me, Philip Semanchuk. Fixes a few bugs (including some + memory leaks) and adds the ability to read the blocking flag on semaphores. + - 1.1.1 (Mar 2007)- Updated setup.py to handle compile options for Linux users. + - 1.1.2 (Nov 2007) - Fixed a sprintf() format in repr to handle size_t. + - 1.1.3 (Nov 2007) - Changed #define of key to _key instead of __key for OS X Leopard. + Thanks to Bill Hart. + - 1.1.4 (Jan 2008) - Changed the reference to key in the ipc_perm structure to the + explicit #define IPC_PERM_KEY_NAME. + + See http://NikitaTheSpider.com/python/shm/ for more thorough documentation, a more Pythonic + wrapper, updates, contact info, setup.py, etc. + + + Module interface: + + - shm.create_memory(int Key, int Size [,int Perm=0666]) --> object + - shm.create_semaphore(int Key [,int Value=1 [,int Perm=0666]]) --> object + - shm.error + - shm.ftok(string Path, int ProjId) --> int + - shm.getsemid(int Key) --> int + - shm.getshmid(int Key) --> int + - shm.memory(int Shmid) --> object + - shm.memory_haskey(int Key) --> int + - shm.remove_memory(int Shmid) --> None + - shm.remove_semaphore(int Semid) --> None + - shm.semaphore(int Semid) --> object + - shm.semaphore_haskey(int Key) --> int + + Memory Objects: + + + Members: + + - m.addr - attachment address in the process address space + - m.attached - 0|1 + - m.cgid - gid of creator + - m.cpid - pid of creator + - m.cuid - uid of creator + - m.gid - gid of owner + - m.key - segment key or IPC_PRIVATE (=0) + - m.lpid - pid of last shmop + - m.nattch - current # of attached processes + - m.perm - operation permission + - m.shmid - shared memory segment id + - m.size - segment size + - m.uid - uid of owner + + + Methods: + + - m.attach([int Addr=0 [,int How=0]]) --> None + - m.detach() --> None + - m.read(int Nbytes [,int Offset=0]) --> string + - m.setgid(int Gid) --> None + - m.setperm(int Perm) --> None + - m.setuid(int Uid) --> None + - m.write(string Data [,int Offset=0]) --> None + + Semaphore Objects: + + + Members: + + - s.cgid - gid of creator + - s.cuid - uid of creator + - s.gid - gid of owner + - s.key - semaphore key or IPC_PRIVATE (=0) + - s.lpid - pid of last semop + - s.ncnt - current # of processes waiting for s.val > 0 + - s.perm - operation permission + - s.semid - semaphore id + - s.uid - uid of owner + - s.val - value of the semaphore counter + - s.zcnt - current # of processes waiting for s.val == 0 + - s.blocking - whether or not the semaphore is in blocking mode + + + Methods: + + - s.P() --> None - blocks if s.val == 0; decrements s.val + - s.V() --> None - increments s.val + - s.Z() --> None - blocks until s.val == 0 + - s.setblocking(0|1) --> None + - s.setgid(int Gid) --> None + - s.setperm(int Perm) --> None + - s.setuid(int Uid) --> None + - s.setundo(0|1) --> None + - s.setval(int Value) --> None + +*/ + +/* ------------------------------------------------------------------------- */ +#include "Python.h" +#include "structmember.h" + +/* v1.1 - added */ +#ifdef __FreeBSD__ +#include /* for system definition of PAGE_SIZE */ +#endif + +#include +#include /* for system's IPC_xxx definitions */ +#include /* for shmget, shmat, shmdt, shmctl */ +#include /* for semget, semctl, semop */ + +/* v1.1.4 - The name of this member varies and is sniffed out by setup.py. */ +#if defined(ZERO_UNDERSCORE_KEY) +#define IPC_PERM_KEY_NAME key +#elif defined(ONE_UNDERSCORE_KEY) +#define IPC_PERM_KEY_NAME _key +#elif defined(TWO_UNDERSCORE_KEY) +#define IPC_PERM_KEY_NAME __key +#endif + +/* +-- Exception type for errors detected by this module. +*/ + +static PyObject *PyShm_Error; + +/* +-- Convenience function to raise an error according to errno. +*/ + +static PyObject * +PyShm_Err(void) +{ + return PyErr_SetFromErrno(PyShm_Error); +} + +/* +-- The object holding a shared memory segment +*/ + +typedef struct { + PyObject_HEAD + int shmid; /* shared memory id */ + int mode; /* attachment mode */ + void *addr; /* shmseg start address */ + struct shmid_ds ds; /* data structure */ +} PyShmMemoryObject; + +staticforward PyTypeObject PyShmMemory_Type; + +#define PyShmObj PyShmMemoryObject +#define PyShmMemory_Check(op) ((op)->ob_type == &PyShmMemory_Type) + +/* +-- The object holding a semaphore +*/ + +typedef struct { + PyObject_HEAD + int semid; /* semaphore id */ + short opflag; /* IPC_NOWAIT, SEM_UNDO */ + struct semid_ds ds; /* data structure */ +} PyShmSemaphoreObject; + +#ifndef HAVE_UNION_SEMUN +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 + +typedef union semun semctl_arg; + +staticforward PyTypeObject PyShmSemaphore_Type; + +#define PyShmSemObj PyShmSemaphoreObject +#define PyShmSemaphore_Check(op) ((op)->ob_type == &PyShmSemaphore_Type) + +/* +-- Internal dictionaries for Python memory and semaphore objects +*/ + +static PyObject *shm_dict = NULL; +static PyObject *sem_dict = NULL; + +/************************************************************/ +/* Memory Objects */ +/************************************************************/ + +/* This is to check the validity of a Python memory object + (and to refresh its data status structure). Notably, we + have to check that the real memory segment it points to + is still in memory and hasn't changed (test its id and + size). It could be that the segment has been removed and + created again by someone else with the same key. This is + fine as far as the segment (1) has the same id and size, + and (2) is accessible via shmctl. If you have a better + test, you're welcome :-) */ + +static int +check_memory_identity(PyShmObj *o) +{ + int new_shmid; + int old_shmid = o->shmid; + int old_size = o->ds.shm_segsz; + key_t old_key = o->ds.shm_perm.IPC_PERM_KEY_NAME; + + /* + -- 1. Try to get the segment identified by the old key (if not IPC_PRIVATE) + -- 2. On failure or on mismatch of the new and the old id -> fail. + -- 3. Try to refresh the object's status using the new id. + -- 4. On failure (the segment cannot be accessed) -> fail. + -- 5. Finaly, compare the old size and the one we got via the new id. + */ + if (old_key != IPC_PRIVATE) { + new_shmid = shmget(old_key, 0, 0); + if (new_shmid != old_shmid) + return 0; + } + else + new_shmid = old_shmid; + if ((shmctl(new_shmid, IPC_STAT, &(o->ds)) != -1) && + (old_size == o->ds.shm_segsz) && + (old_key == o->ds.shm_perm.IPC_PERM_KEY_NAME)) + return 1; + return 0; +} + +/* Convenience macro for updating the shared memory data status structure */ + +#define refresh_memory_status(o) \ + if (!check_memory_identity(o)) { \ + PyErr_SetString(PyShm_Error, "can't access shared memory segment"); \ + return NULL; \ + } + +/* +-- attach([,address=0 [,how=0]]) +*/ + +/* Attach the shared memory segment to the process address space */ + +static PyObject * +PyShmMemory_attach(PyShmObj *self, PyObject *args) +{ + unsigned long address = 0; + int mode = 0; + void *addr, *old_addr; + + if (!PyArg_ParseTuple(args, "|li", &address, &mode)) + return NULL; + refresh_memory_status(self); + /* return if already attached with the same mode to the same address */ + if ((self->addr != NULL) && (self->mode == mode) && + ((address == 0) || (self->addr == (void *)address))) { + Py_INCREF(Py_None); + return Py_None; + } + /* perform the attach */ + addr = (void *)shmat(self->shmid, (void *)address, mode); + if (addr == (void *)-1) + return PyShm_Err(); + old_addr = self->addr; + self->addr = addr; + self->mode = mode; + /* XXX - multiple attachments of the same shared memory segment + to different locations of the process address space is + not supported. */ + shmdt(old_addr); + Py_INCREF(Py_None); + return Py_None; +} + +/* +-- detach() +*/ + +/* Detach the memory object from the process address space */ + +static PyObject * +PyShmMemory_detach(PyShmObj *self, PyObject *args) +{ + if (!PyArg_NoArgs(args)) + return NULL; + if (self->addr != NULL) { + refresh_memory_status(self); + if (shmdt(self->addr) != 0) + return PyShm_Err(); + self->addr = NULL; /* mark as detached */ + } + Py_INCREF(Py_None); + return Py_None; +} + +/* +-- read(int Nbytes [,int Offset=0]) --> string +*/ + +/* Return a string of n bytes peeked from the shared memory segment */ + +static PyObject * +PyShmMemory_read(PyShmObj *self, PyObject *args) +{ + unsigned long n, offset = 0; + char buf[128]; + char *addr; + + if (!PyArg_ParseTuple(args, "l|l", &n, &offset)) + return NULL; + refresh_memory_status(self); + if (self->addr == NULL) { + PyErr_SetString(PyShm_Error, "R/W operation on detached memory"); + return NULL; + } + if ((unsigned long)self->ds.shm_segsz < (n + offset)) { + sprintf(buf, "read() argument%s exceed%s upper memory limit", + offset ? "s" : "", offset ? "" : "s"); + PyErr_SetString(PyShm_Error, buf); + return NULL; + } + addr = (char *)((unsigned long)self->addr + offset); + return PyString_FromStringAndSize(addr, n); +} + +/* +-- setgid(int Gid) +*/ + +static PyObject * +PyShmMemory_setgid(PyShmObj *self, PyObject *args) +{ + long newgid, oldgid; + + if (!PyArg_ParseTuple(args, "l", &newgid)) + return NULL; + refresh_memory_status(self); + oldgid = (long)self->ds.shm_perm.gid; + self->ds.shm_perm.gid = (gid_t)newgid; + if (shmctl(self->shmid, IPC_SET, &(self->ds)) == -1) { + self->ds.shm_perm.gid = (gid_t)oldgid; + return PyShm_Err(); + } + Py_INCREF(Py_None); + return Py_None; +} + +/* +-- setperm(int Perm) +*/ + +static PyObject * +PyShmMemory_setperm(PyShmObj *self, PyObject *args) +{ + long newmode, oldmode; + + if (!PyArg_ParseTuple(args, "l", &newmode)) + return NULL; + refresh_memory_status(self); + newmode &= 0777; /* permission bits only */ + oldmode = (mode_t)self->ds.shm_perm.mode; + self->ds.shm_perm.mode ^= 0777; + self->ds.shm_perm.mode |= (mode_t)newmode; + if (shmctl(self->shmid, IPC_SET, &(self->ds)) == -1) { + self->ds.shm_perm.mode = (mode_t)oldmode; + return PyShm_Err(); + } + Py_INCREF(Py_None); + return Py_None; +} + +/* +-- setuid(int Uid) +*/ + +static PyObject * +PyShmMemory_setuid(PyShmObj *self, PyObject *args) +{ + long newuid, olduid; + + if (!PyArg_ParseTuple(args, "l", &newuid)) + return NULL; + refresh_memory_status(self); + olduid = (long)self->ds.shm_perm.uid; + /* v1.1 - fixed typo that set the group id rather than the user id */ + self->ds.shm_perm.uid = (uid_t)newuid; + if (shmctl(self->shmid, IPC_SET, &(self->ds)) == -1) { + self->ds.shm_perm.uid = (uid_t)olduid; + return PyShm_Err(); + } + Py_INCREF(Py_None); + return Py_None; +} + +/* +-- write(string Data [, int Offset=0]) +*/ + +/* Write a string to the shared memory segment. */ + +static PyObject * +PyShmMemory_write(PyShmObj *self, PyObject *args) +{ + char *data; + unsigned long n, offset = 0; + char buf[128]; + char *addr; + + if (!PyArg_ParseTuple(args, "s#|l", &data, &n, &offset)) + return NULL; + refresh_memory_status(self); + if (self->addr == NULL) { + PyErr_SetString(PyShm_Error, "R/W operation on detached memory"); + return NULL; + } + if (self->mode & SHM_RDONLY) { + PyErr_SetString(PyShm_Error, + "can't write on read-only attached memory"); + return NULL; + } + if ((unsigned long)self->ds.shm_segsz < (n + offset)) { + sprintf(buf, "write() argument%s exceed%s upper memory limit", + offset ? "s" : "", offset ? "" : "s"); + PyErr_SetString(PyShm_Error, buf); + return NULL; + } + addr = (void *)((unsigned long)self->addr + offset); + memcpy(addr, data, n); + Py_INCREF(Py_None); + return Py_None; +} + +/* List of methods for shared memory objects */ + +static PyMethodDef memory_methods[] = { + {"attach", (PyCFunction)PyShmMemory_attach, 1, + "attach([int Addr=0 [,int How=0]]) --> None | except shm.error"}, + {"detach", (PyCFunction)PyShmMemory_detach, 0, + "detach() --> None | except shm.error"}, + {"read", (PyCFunction)PyShmMemory_read, 1, + "read(int Nbytes [,int Offset=0]) --> string | except shm.error"}, + {"setgid", (PyCFunction)PyShmMemory_setgid, 1, + "setgid(int Gid) --> None | except shm.error"}, + {"setperm", (PyCFunction)PyShmMemory_setperm, 1, + "setperm(int Perm) --> None | except shm.error"}, + {"setuid", (PyCFunction)PyShmMemory_setuid, 1, + "setuid(int Uid) --> None | except shm.error"}, + {"write", (PyCFunction)PyShmMemory_write, 1, + "write(string Data [,int Offset=0]) --> None | except shm.error"}, + {NULL, NULL} /* sentinel */ +}; + +#define OFF(x) offsetof(PyShmMemoryObject, x) +#define OFF1(x) OFF(ds) + offsetof(struct shmid_ds, x) +#define OFF2(x) OFF1(shm_perm) + offsetof(struct ipc_perm, x) + +/* List of members for shared memory objects */ + +/* Note: member types are set in the initshm function. + Members which need separate processing are: + - addr --> it is not part of the shmid_ds structure + - attached --> function depending on addr + - nattch --> system dependent declaration in shmid_ds (unknown type) + - perm --> return permission (lower 9) bits only of ds.shm_perm.mode +*/ + +static struct memberlist memory_memberlist[] = { + {"cgid", T_INT, OFF2(cgid), RO}, /* 0 (gid_t) */ + {"cpid", T_INT, OFF1(shm_cpid), RO}, /* 1 (pid_t) */ + {"cuid", T_INT, OFF2(cuid), RO}, /* 2 (uid_t) */ + {"key", T_INT, OFF2(IPC_PERM_KEY_NAME), RO}, /* 3 (key_t) */ + {"lpid", T_INT, OFF1(shm_lpid), RO}, /* 4 (pid_t) */ + {"shmid", T_INT, OFF(shmid), RO}, /* 5 (int) */ + {"size", T_INT, OFF1(shm_segsz), RO}, /* 6 (int) */ + {"gid", T_INT, OFF2(gid), RO}, /* 7 (gid_t) */ + {"uid", T_INT, OFF2(uid), RO}, /* 8 (uid_t) */ + /* The following members are implemented without this table */ + {"addr", T_INT, 0, RO}, /* 9 (void *) */ + {"attached",T_INT, 0, RO}, /* 10 (int) */ + {"nattch", T_INT, 0, RO}, /* 11 sys.dep. */ + {"perm", T_INT, 0, RO}, /* 12 (mode_t) */ + {NULL} /* sentinel */ +}; + +#undef OFF +#undef OFF1 +#undef OFF2 + +static void +PyShmMemory_dealloc(PyShmObj *self) +{ + /* del shm_dict[key], ignore if it fails */ + if (PyDict_DelItem(shm_dict, PyInt_FromLong(self->shmid)) == -1) + PyErr_Clear(); + /* all references in the current process to the shared + memory segment are lost, so if attached, detach it. + XXX: This is not true when Python is embedded. + + if (self->addr != NULL) { + shmdt(self->addr); + } + */ + /* v1.1 - changed the reference below from PyMem_DEL to PyObject_Del. */ + PyObject_Del(self); +} + +static PyObject * +PyShmMemory_getattr(PyShmObj *self, char *name) +{ + PyObject *res; + + res = Py_FindMethod(memory_methods, (PyObject *)self, name); + if (res != NULL) + return res; + PyErr_Clear(); + refresh_memory_status(self); + if (strcmp(name, "attached") == 0) + return PyInt_FromLong((self->addr == NULL) ? 0 : 1); + if (strcmp(name, "addr") == 0) { + if (self->addr != NULL) + return PyInt_FromLong((unsigned long)self->addr); + else { + Py_INCREF(Py_None); + return Py_None; + } + } + if (strcmp(name, "nattch") == 0) + return PyInt_FromLong(self->ds.shm_nattch); + if (strcmp(name, "perm") == 0) + return PyInt_FromLong(self->ds.shm_perm.mode & 0777); + return PyMember_Get((char *)self, memory_memberlist, name); +} + +static PyObject * +PyShmMemory_repr(PyShmObj *self, char *name) +{ + char buf[100]; + char buf2[20]; + + refresh_memory_status(self); + if (self->addr == NULL) + sprintf(buf2, "None"); + else + /* v 1.1 - changed format from %lx to %p. */ + /* v 1.1.2 - changed %u to %zu. */ + sprintf(buf2, "0x%p", self->addr); + sprintf(buf, "<%s shared memory object, id=%d, size=%zu, addr=%s>", + (self->addr == NULL) ? "detached" : (self->mode & SHM_RDONLY) ? + "attached RO" : "attached R/W", + self->shmid, + self->ds.shm_segsz, + buf2); + return PyString_FromString(buf); +} + +/* Type object for shared memory objects */ + +static PyTypeObject PyShmMemory_Type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, /*ob_size*/ + "shared memory", /*tp_name*/ + sizeof(PyShmObj), /*tp_size*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)PyShmMemory_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + (getattrfunc)PyShmMemory_getattr, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + (reprfunc)PyShmMemory_repr, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ +}; + +/************************************************************/ +/* Semaphore Objects */ +/************************************************************/ + +/* This is to check the validity of a Python semaphore object */ + +static int +check_semaphore_identity(PyShmSemObj *o) +{ + int new_semid; + int old_semid = o->semid; + unsigned short old_nsems = o->ds.sem_nsems; + key_t old_key = o->ds.sem_perm.IPC_PERM_KEY_NAME; + semctl_arg arg; + + if (old_key != IPC_PRIVATE) { + new_semid = semget(old_key, 0, 0); + if (new_semid != old_semid) + return 0; + } + else + new_semid = old_semid; + arg.buf = &(o->ds); + if ((semctl(new_semid, 0, IPC_STAT, arg) != -1) && + (old_nsems == o->ds.sem_nsems) && + (old_key == o->ds.sem_perm.IPC_PERM_KEY_NAME)) + return 1; + return 0; +} + +/* Convenience macro for updating the semaphore data status structure */ + +#define refresh_semaphore_status(o) \ + if (!check_semaphore_identity(o)) { \ + PyErr_SetString(PyShm_Error, \ + "can't access semaphore"); \ + return NULL; \ + } + +/* +-- P() +*/ + +static PyObject * +PyShmSemaphore_P(PyShmSemObj *self, PyObject *args) +{ + struct sembuf op[1]; + int res; + + op[0].sem_num = 0; + op[0].sem_op = -1; + op[0].sem_flg = self->opflag; + + if (!PyArg_NoArgs(args)) + return NULL; + refresh_semaphore_status(self); + res = semop(self->semid, op, (size_t)1); + if (res == -1) + return PyShm_Err(); + Py_INCREF(Py_None); + return Py_None; +} + +/* +-- V() +*/ + +static PyObject * +PyShmSemaphore_V(PyShmSemObj *self, PyObject *args) +{ + struct sembuf op[1]; + int res; + + op[0].sem_num = 0; + op[0].sem_op = 1; + op[0].sem_flg = self->opflag; + + if (!PyArg_NoArgs(args)) + return NULL; + refresh_semaphore_status(self); + res = semop(self->semid, op, (size_t)1); + if (res == -1) + return PyShm_Err(); + Py_INCREF(Py_None); + return Py_None; +} + +/* +-- Z() +*/ + +static PyObject * +PyShmSemaphore_Z(PyShmSemObj *self, PyObject *args) +{ + struct sembuf op[1]; + int res; + + op[0].sem_num = 0; + op[0].sem_op = 0; + op[0].sem_flg = self->opflag; + + if (!PyArg_NoArgs(args)) + return NULL; + refresh_semaphore_status(self); + res = semop(self->semid, op, (size_t)1); + if (res == -1) + return PyShm_Err(); + Py_INCREF(Py_None); + return Py_None; +} + +/* +-- setblocking(0|1) +*/ + +static PyObject * +PyShmSemaphore_setblocking(PyShmSemObj *self, PyObject *args) +{ + int block; + + if (!PyArg_ParseTuple(args, "i", &block)) + return NULL; + refresh_semaphore_status(self); + if (block) + self->opflag &= ~IPC_NOWAIT; + else + self->opflag |= IPC_NOWAIT; + Py_INCREF(Py_None); + return Py_None; +} + +/* +-- setgid(int Gid) +*/ + +static PyObject * +PyShmSemaphore_setgid(PyShmSemObj *self, PyObject *args) +{ + long newgid, oldgid; + semctl_arg arg; + + if (!PyArg_ParseTuple(args, "l", &newgid)) + return NULL; + refresh_semaphore_status(self); + oldgid = (long)self->ds.sem_perm.gid; + self->ds.sem_perm.gid = (gid_t)newgid; + arg.buf = &(self->ds); + if (semctl(self->semid, 0, IPC_SET, arg) == -1) { + self->ds.sem_perm.gid = (gid_t)oldgid; + return PyShm_Err(); + } + Py_INCREF(Py_None); + return Py_None; +} + +/* +-- setperm(int Perm) +*/ + +static PyObject * +PyShmSemaphore_setperm(PyShmSemObj *self, PyObject *args) +{ + long newmode, oldmode; + semctl_arg arg; + + if (!PyArg_ParseTuple(args, "l", &newmode)) + return NULL; + refresh_semaphore_status(self); + newmode &= 0777; /* permission bits only */ + oldmode = (mode_t)self->ds.sem_perm.mode; + self->ds.sem_perm.mode ^= 0777; + self->ds.sem_perm.mode |= (mode_t)newmode; + arg.buf = &(self->ds); + if (semctl(self->semid, 0, IPC_SET, arg) == -1) { + self->ds.sem_perm.mode = (mode_t)oldmode; + return PyShm_Err(); + } + Py_INCREF(Py_None); + return Py_None; +} + +/* +-- setuid(int Uid) +*/ + +static PyObject * +PyShmSemaphore_setuid(PyShmSemObj *self, PyObject *args) +{ + long newuid, olduid; + semctl_arg arg; + + if (!PyArg_ParseTuple(args, "l", &newuid)) + return NULL; + refresh_semaphore_status(self); + olduid = (long)self->ds.sem_perm.uid; + /* v1.1 - fixed typo that set the group id rather than the user id */ + self->ds.sem_perm.uid = (uid_t)newuid; + arg.buf = &(self->ds); + if (semctl(self->semid, 0, IPC_SET, arg) == -1) { + self->ds.sem_perm.uid = (uid_t)olduid; + return PyShm_Err(); + } + Py_INCREF(Py_None); + return Py_None; +} + +/* +-- setundo(0|1) +*/ + +static PyObject * +PyShmSemaphore_setundo(PyShmSemObj *self, PyObject *args) +{ + int undo; + + if (!PyArg_ParseTuple(args, "i", &undo)) + return NULL; + refresh_semaphore_status(self); + if (undo) + self->opflag |= SEM_UNDO; + else + self->opflag &= ~SEM_UNDO; + Py_INCREF(Py_None); + return Py_None; +} + +/* +-- setval(int Value) +*/ + +static PyObject * +PyShmSemaphore_setval(PyShmSemObj *self, PyObject *args) +{ + int value; + semctl_arg arg; + + if (!PyArg_ParseTuple(args, "i", &value)) + return NULL; + refresh_semaphore_status(self); + arg.val = value; + if (semctl(self->semid, 0, SETVAL, arg) == -1) + return PyShm_Err(); + Py_INCREF(Py_None); + return Py_None; +} + +/* List of methods for semaphore objects */ + +static PyMethodDef semaphore_methods[] = { + {"P", (PyCFunction)PyShmSemaphore_P, 0, + "P() --> None | except shm.error"}, + {"V", (PyCFunction)PyShmSemaphore_V, 0, + "V() --> None | except shm.error"}, + {"Z", (PyCFunction)PyShmSemaphore_Z, 0, + "Z() --> None | except shm.error"}, + {"setblocking", (PyCFunction)PyShmSemaphore_setblocking, 1, + "setblocking(0|1) --> None"}, + {"setgid", (PyCFunction)PyShmSemaphore_setgid, 1, + "setgid(int Gid) --> None | except shm.error"}, + {"setperm", (PyCFunction)PyShmSemaphore_setperm, 1, + "setperm(int Perm) --> None | except shm.error"}, + {"setuid", (PyCFunction)PyShmSemaphore_setuid, 1, + "setuid(int Uid) --> None | except shm.error"}, + {"setundo", (PyCFunction)PyShmSemaphore_setundo, 1, + "setundo(0|1) --> None"}, + {"setval", (PyCFunction)PyShmSemaphore_setval, 1, + "setval(int Value) --> None | except shm.error"}, + {NULL, NULL} /* sentinel */ +}; + +#define OFF(x) offsetof(PyShmSemaphoreObject, x) +#define OFF1(x) OFF(ds) + offsetof(struct semid_ds, x) +#define OFF2(x) OFF1(sem_perm) + offsetof(struct ipc_perm, x) + +/* List of members for semaphore objects */ + +/* Note: member types are set in the initshm function. + Members which need separate processing are: + - val, lpid, ncnt, zcnt --> in kernel memory, not accessible from a process + - perm --> return permission (lower 9) bits only of ds.sem_perm.mode + - blocking --> in opflags +*/ + +static struct memberlist semaphore_memberlist[] = { + {"cgid", T_INT, OFF2(cgid), RO}, /* 0 (gid_t) */ + {"cuid", T_INT, OFF2(cuid), RO}, /* 1 (uid_t) */ + {"key", T_INT, OFF2(IPC_PERM_KEY_NAME), RO}, /* 2 (key_t) */ + {"semid", T_INT, OFF(semid), RO}, /* 3 (int) */ + {"gid", T_INT, OFF2(gid), RO}, /* 4 (gid_t) */ + {"uid", T_INT, OFF2(uid), RO}, /* 5 (uid_t) */ + /* The following members are implemented without this table */ + {"lpid", T_INT, 0, RO}, /* 6 (ushort) */ + {"ncnt", T_INT, 0, RO}, /* 7 (ushort) */ + {"perm", T_INT, 0, RO}, /* 8 (mode_t) */ + {"val", T_INT, 0, RO}, /* 9 (ushort) */ + {"zcnt", T_INT, 0, RO}, /* 10 (ushort) */ + {"blocking",T_INT, 0, RO}, /* 11 (ushort) */ + {NULL} /* sentinel */ +}; + +#undef OFF +#undef OFF1 +#undef OFF2 + +static void +PyShmSemaphore_dealloc(PyShmSemObj *self) +{ + /* del sem_dict[key], ignore if it fails */ + if (PyDict_DelItem(sem_dict, PyInt_FromLong(self->semid)) == -1) + PyErr_Clear(); + /* v1.1 - changed the reference below from PyMem_DEL to PyObject_Del. */ + PyObject_Del(self); +} + +static PyObject * +PyShmSemaphore_getattr(PyShmSemObj *self, char *name) +{ + PyObject *res; + + res = Py_FindMethod(semaphore_methods, (PyObject *)self, name); + if (res != NULL) + return res; + PyErr_Clear(); + refresh_semaphore_status(self); + if (strcmp(name, "val") == 0) + return PyInt_FromLong(semctl(self->semid, 0, GETVAL, 0)); + if (strcmp(name, "lpid") == 0) + return PyInt_FromLong(semctl(self->semid, 0, GETPID, 0)); + if (strcmp(name, "ncnt") == 0) + return PyInt_FromLong(semctl(self->semid, 0, GETNCNT, 0)); + if (strcmp(name, "zcnt") == 0) + return PyInt_FromLong(semctl(self->semid, 0, GETZCNT, 0)); + if (strcmp(name, "perm") == 0) + return PyInt_FromLong(self->ds.sem_perm.mode & 0777); + /* v1.1 - added blocking */ + if (strcmp(name, "blocking") == 0) + return PyInt_FromLong(self->opflag & IPC_NOWAIT ? 0 : 1); + return PyMember_Get((char *)self, semaphore_memberlist, name); +} + +static PyObject * +PyShmSemaphore_repr(PyShmSemObj *self, char *name) +{ + /* v1.1 - added blocking */ + char buf[200]; + + refresh_semaphore_status(self); + sprintf(buf, "", + self->semid, + semctl(self->semid, 0, GETVAL, 0), + semctl(self->semid, 0, GETNCNT, 0), + semctl(self->semid, 0, GETZCNT, 0), + self->opflag & IPC_NOWAIT ? 0 : 1); + return PyString_FromString(buf); +} + +/* Type object for semaphore objects */ + +static PyTypeObject PyShmSemaphore_Type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, /*ob_size*/ + "semaphore", /*tp_name*/ + sizeof(PyShmSemObj), /*tp_size*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)PyShmSemaphore_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + (getattrfunc)PyShmSemaphore_getattr,/*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + (reprfunc)PyShmSemaphore_repr, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ +}; + +/************************************************************/ +/* Module Interface */ +/************************************************************/ + +/* +-- ftok(string Path, int ProjId) -> int +*/ + +/* Compute a key by using the system's ftok algorithm */ + +static PyObject * +PyShm_ftok(PyObject *self, PyObject *args) +{ + char *path; + char id; + key_t key; + + if (!PyArg_ParseTuple(args, "sb", &path, &id)) + return NULL; + key = ftok(path, id); + return PyInt_FromLong((long)key); +} + +/* +-- getshmid(int Key) --> int | except KeyError +*/ + +/* Return a shared memory segment id from a given key */ + +static PyObject * +PyShm_getshmid(PyObject *self, PyObject *args) +{ + long key; + int shmid; + + if (!PyArg_ParseTuple(args, "l", &key)) + return NULL; + shmid = shmget((key_t)key, 0, 0); + if (shmid == -1) { + PyErr_SetObject(PyExc_KeyError, PyInt_FromLong(key)); + return NULL; + } + return PyInt_FromLong(shmid); +} + +/* +-- memory_haskey(int Key) --> int +*/ + +/* Check whether there is a shared memory segment with the given key */ + +static PyObject * +PyShm_memory_haskey(PyObject *self, PyObject *args) +{ + long key; + int shmid; + + if (!PyArg_ParseTuple(args, "l", &key)) + return NULL; + shmid = shmget((key_t)key, 0, 0); + return PyInt_FromLong((shmid == -1) ? 0 : 1); +} + +/* +-- memory(int Shmid) --> object | except shm.error +*/ + +/* Get an existing shared memory segment and return it as a python object. */ + +static PyObject * +PyShm_memory(PyObject *self, PyObject *args) +{ + int shmid; + PyShmObj *o; + PyObject *keyo; + + if (!PyArg_ParseTuple(args, "i", &shmid)) + return NULL; + keyo = PyInt_FromLong(shmid); + /* get the object from the dictionary */ + if (PyMapping_HasKey(shm_dict, keyo)) { + o = (PyShmObj *)PyDict_GetItem(shm_dict, keyo); + Py_INCREF(o); + } + else { + /* not found, create it */ + if ((o = PyObject_NEW(PyShmObj, &PyShmMemory_Type)) == NULL) + return NULL; + o->shmid = shmid; + o->addr = NULL; + o->mode = 0; + /* shm_dict[shmid] = o */ + if (PyDict_SetItem(shm_dict, keyo, (PyObject *)o) == -1) { + Py_DECREF(o); + PyErr_SetString(PyShm_Error, "can't initialize shared memory object"); + return NULL; + } + Py_DECREF(o); /* the owned reference in shm_dict doesn't count! */ + } + /* v1.1 - plugged memory leak */ + Py_DECREF(keyo); + /* set up the status data */ + if (shmctl(o->shmid, IPC_STAT, &(o->ds)) == -1) { + Py_DECREF(o); + PyErr_SetString(PyShm_Error, "can't access shared memory segment"); + return NULL; + } + return (PyObject *)o; +} + +/* +-- create_memory(int Key, int Size [,int Perm=0666]) --> object +*/ + +/* Create a new shared memory segment. */ + +static PyObject * +PyShm_create_memory(PyObject *self, PyObject *args) +{ + long key; + int size, shmid; + int perm = 0666; /* Default permission is -rw-rw-rw- */ + PyObject *py_shmid; + PyObject *memory; + + if (!PyArg_ParseTuple(args, "li|i", &key, &size, &perm)) + return NULL; + shmid = shmget((key_t)key, size, perm | IPC_CREAT | IPC_EXCL); + if (shmid == -1) + return PyShm_Err(); + /* v1.1 - fixed memory leak resulting from inlining Py_BuildValue */ + py_shmid = Py_BuildValue("(i)", shmid); + memory = PyShm_memory(self, py_shmid); + Py_DECREF(py_shmid); + + return memory; +} + +/* +-- remove_memory(int Shmid) --> None | except shm.error +*/ + +/* Remove an existing shared memory segment. */ + +static PyObject * +PyShm_remove_memory(PyObject *self, PyObject *args) +{ + int shmid, res; + + if (!PyArg_ParseTuple(args, "i", &shmid)) + return NULL; + res = shmctl(shmid, IPC_RMID, 0); /* remove it */ + if (res == -1) + return PyShm_Err(); + Py_INCREF(Py_None); + return Py_None; +} + +/* +-- getsemid(int Key) --> int | except KeyError +*/ + +/* Return a semaphore id from a given key */ + +static PyObject * +PyShm_getsemid(PyObject *self, PyObject *args) +{ + long key; + int semid; + + if (!PyArg_ParseTuple(args, "l", &key)) + return NULL; + semid = semget((key_t)key, 0, 0); + if (semid == -1) { + PyErr_SetObject(PyExc_KeyError, PyInt_FromLong(key)); + return NULL; + } + return PyInt_FromLong(semid); +} + +/* +-- semaphore_haskey(int Key) --> int +*/ + +/* Check whether there is a semaphore with the given key */ + +static PyObject * +PyShm_semaphore_haskey(PyObject *self, PyObject *args) +{ + long key; + int semid; + + if (!PyArg_ParseTuple(args, "l", &key)) + return NULL; + semid = semget((key_t)key, 0, 0); + return PyInt_FromLong((semid == -1) ? 0 : 1); +} + +/* +-- semaphore(int Semid) --> object +*/ + +/* Get an existing semaphore and return it as a python object. */ + +static PyObject * +PyShm_semaphore(PyObject *self, PyObject *args) +{ + int semid; + PyShmSemObj *o; + PyObject *keyo; + semctl_arg arg; + + if (!PyArg_ParseTuple(args, "i", &semid)) + return NULL; + keyo = PyInt_FromLong(semid); + /* get the object from the dictionary */ + if (PyMapping_HasKey(sem_dict, keyo)) { + o = (PyShmSemObj *)PyDict_GetItem(sem_dict, keyo); + Py_INCREF(o); + } + else { + /* not found, create it */ + if ((o = PyObject_NEW(PyShmSemObj, &PyShmSemaphore_Type)) == NULL) + return NULL; + o->semid = semid; + o->opflag = 0; + /* sem_dict[semid] = o */ + if (PyDict_SetItem(sem_dict, keyo, (PyObject *)o) == -1) { + Py_DECREF(o); + PyErr_SetString(PyShm_Error, "can't initialize semaphore object"); + return NULL; + } + Py_DECREF(o); /* the owned reference in sem_dict doesn't count! */ + } + /* v1.1 - plugged memory leak */ + Py_DECREF(keyo); + /* set up the status data */ + arg.buf = &(o->ds); + if (semctl(o->semid, 0, IPC_STAT, arg) == -1) { + Py_DECREF(o); + PyErr_SetString(PyShm_Error, "can't access semaphore"); + return NULL; + } + return (PyObject *)o; +} + +/* +-- create_semaphore(int Key, [,int Value=1 [,int Perm=0666]]) --> object +*/ + +/* Create a new semaphore. */ + +static PyObject * +PyShm_create_semaphore(PyObject *self, PyObject *args) +{ + long key; + int semid; + int value = 1; + int perm = 0666; /* Default permission is -rw-rw-rw- */ + semctl_arg arg; + PyObject *py_shmid; + PyObject *semaphore; + + if (!PyArg_ParseTuple(args, "l|ii", &key, &value, &perm)) + return NULL; + semid = semget((key_t)key, 1, perm | IPC_CREAT | IPC_EXCL); + arg.val = value; + if (!((semid != -1) && (semctl(semid, 0, SETVAL, arg) != -1))) + return PyShm_Err(); + /* v1.1 - fixed memory leak resulting from inlining Py_BuildValue */ + py_shmid = Py_BuildValue("(i)", semid); + semaphore = PyShm_semaphore(self, py_shmid); + Py_DECREF(py_shmid); + + return semaphore; +} + +/* +-- remove_semaphore(int Semid) --> None | except shm.error +*/ + +/* Remove an existing semaphore. */ + +static PyObject * +PyShm_remove_semaphore(PyObject *self, PyObject *args) +{ + int semid, res; + + if (!PyArg_ParseTuple(args, "i", &semid)) + return NULL; + res = semctl(semid, 0, IPC_RMID, 0); /* remove it */ + if (res == -1) + return PyShm_Err(); + Py_INCREF(Py_None); + return Py_None; +} + +/* List of functions exported by this module. */ + +static PyMethodDef PyShm_methods[] = { + {"create_memory", PyShm_create_memory, 1, + "create_memory(int Key, int Size [,int Perm=0666]) --> object | except shm.error"}, + {"create_semaphore", PyShm_create_semaphore, 1, + "create_semaphore(int Key [,int Value=1 [,int Perm=0666]]) --> object | except shm.error"}, + {"ftok", PyShm_ftok, 1, + "ftok(string Path, int ProjId) --> int | except shm.error"}, + {"getsemid", PyShm_getsemid, 1, + "getsemid(int Key) --> int | except KeyError"}, + {"getshmid", PyShm_getshmid, 1, + "getshmid(int Key) --> int | except KeyError"}, + {"memory_haskey", PyShm_memory_haskey, 1, + "memory_haskey(int Key) --> int"}, + {"memory", PyShm_memory, 1, + "memory(int Shmid) --> object | except shm.error"}, + {"semaphore", PyShm_semaphore, 1, + "semaphore(int Semid) --> object | except shm.error"}, + {"semaphore_haskey", PyShm_semaphore_haskey, 1, + "semaphore_haskey(int Key) --> int"}, + {"remove_memory", PyShm_remove_memory, 1, + "remove_memory(int Shmid) --> None | except shm.error"}, + {"remove_semaphore", PyShm_remove_semaphore, 1, + "remove_semaphore(int Semid) --> None | except shm.error"}, + {NULL, NULL} /* Sentinel */ +}; + + +/* Initialize this module */ + +/* This is for inserting constants in the module's dictionary */ + +static void +insint(PyObject *d, char *name, int value) +{ + PyObject *v = PyInt_FromLong((long) value); + if (!v || PyDict_SetItemString(d, name, v)) + Py_FatalError("can't initialize shm module"); + + Py_DECREF(v); +} + +/* This is to set up the type of shared memory/semaphore object members */ + +static void +set_member_type(struct memberlist *sxm_memberlist, + int index, /* index in memberlist */ + int typesize /* sizeof(member_type) */ + ) +{ + int t; + + if (typesize == sizeof(char)) + t = T_UBYTE; + else if (typesize == sizeof(short)) + t = T_USHORT; + else if (typesize == sizeof(int)) + t = T_UINT; + else if (typesize == sizeof(long)) + t = T_ULONG; + else + Py_FatalError("can't initialize shm module"); + sxm_memberlist[index].type = t; +}; + +void +initshm(void) +{ + PyObject *m, *d; + + m = Py_InitModule("shm", PyShm_methods); + d = PyModule_GetDict(m); + PyShm_Error = PyString_FromString("shm.error"); + if (PyShm_Error == NULL || + PyDict_SetItemString(d, "error", PyShm_Error) != 0) + Py_FatalError("can't define shm.error"); + if (PyDict_SetItemString(d, "__doc__", PyString_FromString + ("Interface to System V shared memory IPC")) != 0) + Py_FatalError("can't define shm.__doc__"); + if ((shm_dict = PyDict_New()) == NULL || (sem_dict = PyDict_New()) == NULL) + Py_FatalError("can't initialize shm module"); + + /* initialize the machine dependent types in memory_memberlist */ + set_member_type(memory_memberlist, 0, sizeof(gid_t)); /* cgid */ + set_member_type(memory_memberlist, 1, sizeof(pid_t)); /* cpid */ + set_member_type(memory_memberlist, 2, sizeof(uid_t)); /* cuid */ + set_member_type(memory_memberlist, 3, sizeof(key_t)); /* key */ + set_member_type(memory_memberlist, 4, sizeof(pid_t)); /* lpid */ + set_member_type(memory_memberlist, 5, sizeof(int)); /* shmid */ + set_member_type(memory_memberlist, 6, sizeof(int)); /* size */ + set_member_type(memory_memberlist, 7, sizeof(gid_t)); /* gid */ + set_member_type(memory_memberlist, 8, sizeof(uid_t)); /* uid */ + + /* initialize the machine dependent types in semaphore_memberlist */ + set_member_type(semaphore_memberlist, 0, sizeof(gid_t)); /* cgid */ + set_member_type(semaphore_memberlist, 1, sizeof(uid_t)); /* cuid */ + set_member_type(semaphore_memberlist, 2, sizeof(key_t)); /* key */ + set_member_type(semaphore_memberlist, 3, sizeof(int)); /* semid */ + set_member_type(semaphore_memberlist, 4, sizeof(gid_t)); /* gid */ + set_member_type(semaphore_memberlist, 5, sizeof(uid_t)); /* uid */ + + /* relevant constants for this module; the others are useless here */ + insint(d, "IPC_PRIVATE", IPC_PRIVATE); + insint(d, "SHM_RDONLY", SHM_RDONLY); + insint(d, "SHM_RND", SHM_RND); +#ifdef SHMLBA + insint(d, "SHMLBA", SHMLBA); +#endif +#ifdef SEM_A + insint(d, "SEM_A", SEM_A); +#endif +#ifdef SEM_R + insint(d, "SEM_R", SEM_R); +#endif +#ifdef SHM_R + insint(d, "SHM_R", SHM_R); +#endif +#ifdef SHM_W + insint(d, "SHM_W", SHM_W); +#endif +} diff --git a/doc/TODO b/doc/TODO index 6027737..6a959ca 100644 --- a/doc/TODO +++ b/doc/TODO @@ -16,6 +16,7 @@ Backend: - save/restore queue on exit/start - only remove already merged packages from queue - make sure, a package being removed from the queue is not needed as a dependency by another package +- binary package support - "nach hause telefonieren" :) @@ -38,6 +39,8 @@ GTK: - show dependencies - reload package table when emerge is finished - dependency tree out of the installed packages +- better display for search results + Qt (stopped): --- diff --git a/etc/portato.cfg b/etc/portato.cfg index 25b9f89..8870757 100644 --- a/etc/portato.cfg +++ b/etc/portato.cfg @@ -73,4 +73,13 @@ browserCmd = firefox ; sets the font of the console - string values consolefont = Monospace 11 +; sets the position of the tabs of the two notebooks +; allowed positions: +; - 1 : top +; - 2 : bottom +; - 3 : left +; - 4 : right +packagetabpos = 2 +systemtabpos = 2 + # vim:ts=4:sw=4:ft=cfg diff --git a/portato.py b/portato.py index 42726ee..1e3a7c9 100755 --- a/portato.py +++ b/portato.py @@ -102,7 +102,7 @@ def main (): try: import shm_wrapper as shm except ImportError: - from portato.shm import shm_wrapper as shm + from portato._shm import shm_wrapper as shm mem = shm.create_memory(1024, permissions=0600) sig = shm.create_semaphore(InitialValue = 0, permissions = 0600) diff --git a/portato/gui/gtk/dialogs.py b/portato/gui/gtk/dialogs.py index 7bea2b8..7a1f28d 100644 --- a/portato/gui/gtk/dialogs.py +++ b/portato/gui/gtk/dialogs.py @@ -68,6 +68,12 @@ def remove_deps_dialog (): infoMB.destroy() return ret +def remove_updates_dialog(): + askMB = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO, _("This is the updates queue. You cannot remove single elements.\nDo you want to clear the whole queue instead?")) + ret = askMB.run() + askMB.destroy() + return ret + def remove_queue_dialog (): askMB = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO, _("Do you really want to clear the whole queue?")) ret = askMB.run() diff --git a/portato/gui/gtk/windows.py b/portato/gui/gtk/windows.py index 3f5255f..d8366a0 100644 --- a/portato/gui/gtk/windows.py +++ b/portato/gui/gtk/windows.py @@ -37,7 +37,7 @@ from .exception_handling import GtkThread from .views import LogView, HighlightView, InstalledOnlyView from .dialogs import (blocked_dialog, changed_flags_dialog, io_ex_dialog, nothing_found_dialog, queue_not_empty_dialog, remove_deps_dialog, - remove_queue_dialog, unmask_dialog) + remove_queue_dialog, remove_updates_dialog, unmask_dialog) class AboutWindow (AbstractDialog): """A window showing the "about"-informations.""" @@ -304,23 +304,37 @@ class PreferenceWindow (AbstractDialog): "browserEdit" : ("browserCmd", "GUI") } - def __init__ (self, parent, cfg, set_console_font): + # the mappings for the tabpos combos + tabpos = { + 1 : gtk.POS_TOP, + 2 : gtk.POS_BOTTOM, + 3 : gtk.POS_LEFT, + 4 : gtk.POS_RIGHT + } + + def __init__ (self, parent, cfg, console_fn, linkbtn_fn, tabpos_fn): """Constructor. @param parent: parent window @type parent: gtk.Window @param cfg: configuration object @type cfg: gui_helper.Config - @param set_console_font: function to call to set the console font - @type set_console_font: function(string)""" + @param console_fn: function to call to set the console font + @type console_fn: function(string) + @param linkbtn_fn: function to call to set the linkbutton behavior + @type linkbtn_fn: function(string) + @param tabpos_fn: function to call to set the tabposition of the notebooks + @type tabpos_fn: function(gtk.ComboBox,int)""" AbstractDialog.__init__(self, parent) # our config self.cfg = cfg - # the console font setter - self.set_console_font = set_console_font + # the setter functions + self.console_fn = console_fn + self.linkbtn_fn = linkbtn_fn + self.tabpos_fn = tabpos_fn # set the bg-color of the hint hintEB = self.tree.get_widget("hintEB") @@ -348,6 +362,19 @@ class PreferenceWindow (AbstractDialog): self.consoleFontBtn = self.tree.get_widget("consoleFontBtn") self.consoleFontBtn.set_font_name(self.cfg.get("consolefont", section = "GTK")) + # the comboboxes + self.systemTabCombo = self.tree.get_widget("systemTabCombo") + self.pkgTabCombo = self.tree.get_widget("packageTabCombo") + + for c in (self.systemTabCombo, self.pkgTabCombo): + m = c.get_model() + m.clear() + for i in (_("Top"), _("Bottom"), _("Left"), _("Right")): + m.append((i,)) + + self.systemTabCombo.set_active(int(self.cfg.get("systemTabPos", section = "GTK"))-1) + self.pkgTabCombo.set_active(int(self.cfg.get("packageTabPos", section = "GTK"))-1) + self.window.show_all() def _save(self): @@ -367,9 +394,17 @@ class PreferenceWindow (AbstractDialog): font = self.consoleFontBtn.get_font_name() self.cfg.set("consolefont", font, section = "GTK") - self.set_console_font(font) + self.console_fn(font) + + pkgPos = self.pkgTabCombo.get_active()+1 + sysPos = self.systemTabCombo.get_active()+1 + + self.cfg.set("packageTabPos", str(pkgPos), section = "GTK") + self.cfg.set("systemTabPos", str(sysPos), section = "GTK") + + self.tabpos_fn(map(self.tabpos.get, (pkgPos, sysPos))) - gtk.link_button_set_uri_hook(lambda btn, x: get_listener().send_cmd([self.cfg.get("browserCmd", section = "GUI"), btn.get_uri()])) + self.linkbtn_fn(self.cfg.get("browserCmd", section="GUI")) def cb_ok_clicked(self, button): """Saves, writes to config-file and closes the window.""" @@ -642,15 +677,15 @@ class PackageTable: if emerge: try: try: - self.queue.append(self.actual_package().get_cpv(), unmerge = False, update = update) + self.queue.append(self.actual_package().get_cpv(), type = "install", update = update) except PackageNotFoundException, e: if unmask_dialog(e[0]) == gtk.RESPONSE_YES: - self.queue.append(self.actual_package().get_cpv(), unmerge = False, unmask = True, update = update) + self.queue.append(self.actual_package().get_cpv(), type = "install", unmask = True, update = update) except BlockedException, e: blocked_dialog(e[0], e[1]) else: try: - self.queue.append(self.actual_package().get_cpv(), unmerge = True) + self.queue.append(self.actual_package().get_cpv(), type = "uninstall") except PackageNotFoundException, e: error(_("Package could not be found: %s"), e[0]) #masked_dialog(e[0]) @@ -752,9 +787,9 @@ class PackageTable: self.actual_package().remove_new_use_flags() self.actual_package().remove_new_masked() self.actual_package().remove_new_testing() - self.versList.get_model().clear() - self.fill_vers_list() - self.cb_vers_list_changed() + self.versionCombo.get_model().clear() + self.fill_version_combo() + self.cb_version_combo_changed() if self.instantChange: self._update_keywords(True, update = True) return True @@ -762,13 +797,13 @@ class PackageTable: def cb_package_emerge_clicked (self, button): """Callback for pressed emerge-button. Adds the package to the EmergeQueue.""" self._update_keywords(True) - self.main.notebook.set_current_page(self.main.QUEUE_PAGE) + self.main.sysNotebook.set_current_page(self.main.QUEUE_PAGE) return True def cb_package_unmerge_clicked (self, button): """Callback for pressed unmerge-button clicked. Adds the package to the UnmergeQueue.""" self._update_keywords(False) - self.main.notebook.set_current_page(self.main.QUEUE_PAGE) + self.main.sysNotebook.set_current_page(self.main.QUEUE_PAGE) return True def cb_testing_toggled (self, button): @@ -978,8 +1013,9 @@ class MainWindow (Window): splash(_("Finishing startup")) - # notebook - self.notebook = self.tree.get_widget("systemNotebook") + # notebooks + self.sysNotebook = self.tree.get_widget("systemNotebook") + self.pkgNotebook = self.tree.get_widget("packageNotebook") self.window.show_all() # the hidden stuff @@ -1025,7 +1061,7 @@ class MainWindow (Window): self.queueList.set_model(store) cell = gtk.CellRendererText() - col = gtk.TreeViewColumn(_("Queue"), cell, text = 0) + col = gtk.TreeViewColumn(_("Queue"), cell, markup = 0) self.queueList.append_column(col) col = gtk.TreeViewColumn(_("Options"), cell, markup = 1) @@ -1163,6 +1199,13 @@ class MainWindow (Window): """Is called when we want to jump to a specific package.""" self.show_package(cp, self.queue, version = version) + def set_uri_hook (self, browser): + gtk.link_button_set_uri_hook(lambda btn, x: get_listener().send_cmd([browser, btn.get_uri()])) + + def set_notebook_tabpos (self, tabposlist): + self.pkgNotebook.set_tab_pos(tabposlist[0]) + self.sysNotebook.set_tab_pos(tabposlist[1]) + def title_update (self, title): def window_title_update (title): @@ -1189,7 +1232,7 @@ class MainWindow (Window): else: title = (_("Console (%(title)s)") % {"title" : title}) - self.notebook.set_tab_label_text(self.termHB, title) + self.sysNotebook.set_tab_label_text(self.termHB, title) return False @@ -1297,10 +1340,8 @@ class MainWindow (Window): tooltip.set_markup(string) return string != "" - def cb_emerge_clicked (self, action): - """Do emerge.""" - - self.notebook.set_current