1 #define PY_SSIZE_T_CLEAN
2 #include "Python.h"
3 #include "structmember.h"
4 
5 #include "common.h"
6 #include "memory.h"
7 
8 
9 /******************    Internal use only     **********************/
10 PyObject *
shm_str(SharedMemory * self)11 shm_str(SharedMemory *self) {
12 	return PyUnicode_FromFormat("Key=%ld, id=%d", (long)self->key, self->id);
13 }
14 
15 PyObject *
shm_repr(SharedMemory * self)16 shm_repr(SharedMemory *self) {
17     return PyUnicode_FromFormat("sysv_ipc.SharedMemory(%ld)", (long)self->key);
18 }
19 
20 PyObject *
shm_attach(SharedMemory * self,void * address,int shmat_flags)21 shm_attach(SharedMemory *self, void *address, int shmat_flags) {
22     DPRINTF("attaching memory @ address %p with id %d using flags 0x%x\n",
23              address, self->id, shmat_flags);
24 
25     self->address = shmat(self->id, address, shmat_flags);
26 
27     if ((void *)-1 == self->address) {
28         self->address = NULL;
29         switch (errno) {
30             case EACCES:
31                 PyErr_SetString(pPermissionsException, "No permission to attach");
32             break;
33 
34             case ENOMEM:
35                 PyErr_SetString(PyExc_MemoryError, "Not enough memory");
36             break;
37 
38             case EINVAL:
39                 PyErr_SetString(PyExc_ValueError, "Invalid id, address, or flags");
40             break;
41 
42             default:
43                 PyErr_SetFromErrno(PyExc_OSError);
44             break;
45         }
46 
47         goto error_return;
48     }
49     else {
50         // memory was attached successfully
51         self->read_only = (shmat_flags & SHM_RDONLY) ? 1 : 0;
52         DPRINTF("set memory's internal read_only flag to %d\n", self->read_only);
53     }
54 
55     Py_RETURN_NONE;
56 
57     error_return:
58     return NULL;
59 }
60 
61 
62 PyObject *
shm_remove(int shared_memory_id)63 shm_remove(int shared_memory_id) {
64     struct shmid_ds shm_info;
65 
66     DPRINTF("removing shm with id %d\n", shared_memory_id);
67     if (-1 == shmctl(shared_memory_id, IPC_RMID, &shm_info)) {
68         switch (errno) {
69             case EIDRM:
70             case EINVAL:
71                 PyErr_Format(pExistentialException,
72                              "No shared memory with id %d exists",
73                              shared_memory_id);
74             break;
75 
76             case EPERM:
77                 PyErr_SetString(pPermissionsException,
78                                 "You do not have permission to remove the shared memory");
79             break;
80 
81             default:
82                 PyErr_SetFromErrno(PyExc_OSError);
83             break;
84         }
85         goto error_return;
86     }
87 
88     Py_RETURN_NONE;
89 
90     error_return:
91     return NULL;
92 }
93 
94 
95 static PyObject *
shm_get_value(int shared_memory_id,enum GET_SET_IDENTIFIERS field)96 shm_get_value(int shared_memory_id, enum GET_SET_IDENTIFIERS field) {
97 	// Gets one of the values in GET_SET_IDENTIFIERS and returns it as a boxed Python int or long.
98 	// The caller assumes responsibility for the reference.
99 	// If an error occurs, sets the Python error and returns NULL.
100     struct shmid_ds shm_info;
101     PyObject *py_value = NULL;
102 
103     DPRINTF("Calling shmctl(...IPC_STAT...), field = %d\n", field);
104     if (-1 == shmctl(shared_memory_id, IPC_STAT, &shm_info)) {
105         switch (errno) {
106             case EIDRM:
107             case EINVAL:
108                 PyErr_Format(pExistentialException,
109                              "No shared memory with id %d exists",
110                              shared_memory_id);
111             break;
112 
113             case EACCES:
114                 PyErr_SetString(pPermissionsException,
115                                 "You do not have permission to read the shared memory attribute");
116             break;
117 
118             default:
119                 PyErr_SetFromErrno(PyExc_OSError);
120             break;
121         }
122 
123         goto error_return;
124     }
125 
126     switch (field) {
127         case SVIFP_SHM_SIZE:
128             py_value = SIZE_T_TO_PY(shm_info.shm_segsz);
129         break;
130 
131         case SVIFP_SHM_LAST_ATTACH_TIME:
132             py_value = TIME_T_TO_PY(shm_info.shm_atime);
133         break;
134 
135         case SVIFP_SHM_LAST_DETACH_TIME:
136             py_value = TIME_T_TO_PY(shm_info.shm_dtime);
137         break;
138 
139         case SVIFP_SHM_LAST_CHANGE_TIME:
140             py_value = TIME_T_TO_PY(shm_info.shm_ctime);
141         break;
142 
143         case SVIFP_SHM_CREATOR_PID:
144             py_value = PID_T_TO_PY(shm_info.shm_cpid);
145         break;
146 
147         case SVIFP_SHM_LAST_AT_DT_PID:
148             py_value = PID_T_TO_PY(shm_info.shm_lpid);
149         break;
150 
151         case SVIFP_SHM_NUMBER_ATTACHED:
152             // shm_nattch is unsigned
153             // ref: http://www.opengroup.org/onlinepubs/007908799/xsh/sysshm.h.html
154             py_value = PyLong_FromUnsignedLong(shm_info.shm_nattch);
155         break;
156 
157         case SVIFP_IPC_PERM_UID:
158             py_value = UID_T_TO_PY(shm_info.shm_perm.uid);
159         break;
160 
161         case SVIFP_IPC_PERM_GID:
162             py_value = GID_T_TO_PY(shm_info.shm_perm.gid);
163         break;
164 
165         case SVIFP_IPC_PERM_CUID:
166             py_value = UID_T_TO_PY(shm_info.shm_perm.cuid);
167         break;
168 
169         case SVIFP_IPC_PERM_CGID:
170             py_value = GID_T_TO_PY(shm_info.shm_perm.cgid);
171         break;
172 
173         case SVIFP_IPC_PERM_MODE:
174             py_value = MODE_T_TO_PY(shm_info.shm_perm.mode);
175         break;
176 
177         default:
178             PyErr_Format(pInternalException, "Bad field %d passed to shm_get_value", field);
179             goto error_return;
180         break;
181     }
182 
183     return py_value;
184 
185     error_return:
186     return NULL;
187 }
188 
189 
190 static int
shm_set_ipc_perm_value(int id,enum GET_SET_IDENTIFIERS field,union ipc_perm_value value)191 shm_set_ipc_perm_value(int id, enum GET_SET_IDENTIFIERS field, union ipc_perm_value value) {
192     struct shmid_ds shm_info;
193 
194     if (-1 == shmctl(id, IPC_STAT, &shm_info)) {
195         switch (errno) {
196             case EIDRM:
197             case EINVAL:
198                 PyErr_Format(pExistentialException,
199                              "No shared memory with id %d exists", id);
200             break;
201 
202             case EACCES:
203                 PyErr_SetString(pPermissionsException,
204                                 "You do not have permission to read the shared memory attribute");
205             break;
206 
207             default:
208                 PyErr_SetFromErrno(PyExc_OSError);
209             break;
210         }
211         goto error_return;
212     }
213 
214     switch (field) {
215         case SVIFP_IPC_PERM_UID:
216             shm_info.shm_perm.uid = value.uid;
217         break;
218 
219         case SVIFP_IPC_PERM_GID:
220             shm_info.shm_perm.gid = value.gid;
221         break;
222 
223         case SVIFP_IPC_PERM_MODE:
224             shm_info.shm_perm.mode = value.mode;
225         break;
226 
227         default:
228             PyErr_Format(pInternalException,
229                          "Bad field %d passed to shm_set_ipc_perm_value",
230                          field);
231             goto error_return;
232         break;
233     }
234 
235     if (-1 == shmctl(id, IPC_SET, &shm_info)) {
236         switch (errno) {
237             case EIDRM:
238             case EINVAL:
239                 PyErr_Format(pExistentialException,
240                              "No shared memory with id %d exists", id);
241             break;
242 
243             case EPERM:
244                 PyErr_SetString(pPermissionsException,
245                                 "You do not have permission to change the shared memory's attributes");
246             break;
247 
248             default:
249                 PyErr_SetFromErrno(PyExc_OSError);
250             break;
251         }
252 
253         goto error_return;
254     }
255 
256     return 0;
257 
258     error_return:
259     return -1;
260 }
261 
262 int
shm_get_buffer(SharedMemory * self,Py_buffer * view,int flags)263 shm_get_buffer(SharedMemory *self, Py_buffer *view, int flags)
264 // Implementation of buffer interface (getbufferproc). The buffer implementation in Python 3.x was
265 // backported to 2.7, and the 3.x documentation is more complete and easier to understand.
266 // https://docs.python.org/3/c-api/typeobj.html#buffer-structs
267 {
268 	PyObject *py_size = shm_get_value(self->id, SVIFP_SHM_SIZE);
269 	Py_ssize_t size;
270 
271     if (!py_size) {
272     	// If shm_get_value() failed, the Python error will already be set so there's no
273     	// need for me to set it here.
274         return -1;
275     }
276     else {
277     	size = PyLong_AsSsize_t(py_size);
278     	Py_DECREF(py_size);
279 	    return PyBuffer_FillInfo(view,
280 	    						 (PyObject *)self,
281 	                             self->address,
282 	                             size,
283 	                             0,
284 	                             flags);
285 	}
286 }
287 
288 
289 /******************    Class methods     **********************/
290 
291 
292 void
SharedMemory_dealloc(SharedMemory * self)293 SharedMemory_dealloc(SharedMemory *self) {
294     Py_TYPE(self)->tp_free((PyObject*)self);
295 }
296 
297 PyObject *
SharedMemory_new(PyTypeObject * type,PyObject * args,PyObject * kwlist)298 SharedMemory_new(PyTypeObject *type, PyObject *args, PyObject *kwlist) {
299     SharedMemory *self;
300 
301     self = (SharedMemory *)type->tp_alloc(type, 0);
302 
303     if (NULL != self) {
304         self->key = (key_t)-1;
305         self->id = 0;
306         self->read_only = 0;
307         self->address = NULL;
308     }
309 
310     return (PyObject *)self;
311 }
312 
313 
314 int
SharedMemory_init(SharedMemory * self,PyObject * args,PyObject * keywords)315 SharedMemory_init(SharedMemory *self, PyObject *args, PyObject *keywords) {
316     NoneableKey key;
317     int mode = 0600;
318     unsigned long size = 0;
319     int shmget_flags = 0;
320     int shmat_flags = 0;
321     char init_character = ' ';
322     char *keyword_list[ ] = {"key", "flags", "mode", "size", "init_character", NULL};
323     PyObject *py_size = NULL;
324 
325     DPRINTF("Inside SharedMemory_init()\n");
326 
327     if (!PyArg_ParseTupleAndKeywords(args, keywords, "O&|iikc", keyword_list,
328                                      &convert_key_param, &key,
329                                      &shmget_flags, &mode, &size,
330                                      &init_character))
331         goto error_return;
332 
333     mode &= 0777;
334     shmget_flags &= ~0777;
335 
336     DPRINTF("key is none = %d, key value = %ld\n", key.is_none, (long)key.value);
337 
338     if ( !(shmget_flags & IPC_CREAT) && (shmget_flags & IPC_EXCL) ) {
339         PyErr_SetString(PyExc_ValueError,
340                 "IPC_EXCL must be combined with IPC_CREAT");
341         goto error_return;
342     }
343 
344     if (key.is_none && ((shmget_flags & IPC_EXCL) != IPC_EXCL)) {
345         PyErr_SetString(PyExc_ValueError,
346                 "Key can only be None if IPC_EXCL is set");
347         goto error_return;
348     }
349 
350     // When creating a new segment, the default size is PAGE_SIZE.
351     if (((shmget_flags & IPC_CREX) == IPC_CREX) && (!size))
352         size = PAGE_SIZE;
353 
354     if (key.is_none) {
355         // (key == None) ==> generate a key for the caller
356         do {
357             errno = 0;
358             self->key = get_random_key();
359 
360             DPRINTF("Calling shmget, key=%ld, size=%lu, mode=%o, flags=0x%x\n",
361                     (long)self->key, size, mode, shmget_flags);
362             self->id = shmget(self->key, size, mode | shmget_flags);
363         } while ( (-1 == self->id) && (EEXIST == errno) );
364     }
365     else {
366         // (key != None) ==> use key supplied by the caller
367         self->key = key.value;
368 
369         DPRINTF("Calling shmget, key=%ld, size=%lu, mode=%o, flags=0x%x\n",
370                 (long)self->key, size, mode, shmget_flags);
371         self->id = shmget(self->key, size, mode | shmget_flags);
372     }
373 
374     DPRINTF("id == %d\n", self->id);
375 
376     if (self->id == -1) {
377         switch (errno) {
378             case EACCES:
379                 PyErr_Format(pPermissionsException,
380                              "Permission %o cannot be granted on the existing segment",
381                              mode);
382             break;
383 
384             case EEXIST:
385                 PyErr_Format(pExistentialException,
386                     "Shared memory with the key %ld already exists",
387                     (long)self->key);
388             break;
389 
390             case ENOENT:
391                 PyErr_Format(pExistentialException,
392                     "No shared memory exists with the key %ld", (long)self->key);
393             break;
394 
395             case EINVAL:
396                 PyErr_SetString(PyExc_ValueError, "The size is invalid");
397             break;
398 
399             case ENOMEM:
400                 PyErr_SetString(PyExc_MemoryError, "Not enough memory");
401             break;
402 
403             case ENOSPC:
404                 PyErr_SetString(PyExc_OSError,
405                     "Not enough shared memory identifiers available (ENOSPC)");
406             break;
407 
408             default:
409                 PyErr_SetFromErrno(PyExc_OSError);
410             break;
411         }
412         goto error_return;
413     }
414 
415     // Attach the memory. If no write permissions requested, attach read-only.
416     shmat_flags = (mode & 0200) ? 0 : SHM_RDONLY;
417     if (NULL == shm_attach(self, NULL, shmat_flags)) {
418         // Bad news, something went wrong.
419         goto error_return;
420     }
421 
422     if ( ((shmget_flags & IPC_CREX) == IPC_CREX) && (!(shmat_flags & SHM_RDONLY)) ) {
423         // Initialize the memory.
424 
425         py_size = shm_get_value(self->id, SVIFP_SHM_SIZE);
426 
427         if (!py_size)
428             goto error_return;
429         else {
430             size = PyLong_AsUnsignedLongMask(py_size);
431 
432             DPRINTF("memsetting address %p to %lu bytes of ASCII 0x%x (%c)\n", \
433                     self->address, size, (int)init_character, init_character);
434             memset(self->address, init_character, size);
435         }
436 
437         Py_DECREF(py_size);
438     }
439 
440     return 0;
441 
442     error_return:
443     return -1;
444 }
445 
446 
447 PyObject *
SharedMemory_attach(SharedMemory * self,PyObject * args,PyObject * keywords)448 SharedMemory_attach(SharedMemory *self, PyObject *args, PyObject *keywords) {
449     PyObject *py_address = NULL;
450     void *address = NULL;
451     int flags = 0;
452     static char *keyword_list[ ] = {"address", "flags", NULL};
453 
454     DPRINTF("Inside SharedMemory_attach()\n");
455 
456     if (!PyArg_ParseTupleAndKeywords(args, keywords, "|Oi", keyword_list,
457                                      &py_address, &flags))
458         goto error_return;
459 
460     if ((!py_address) || (py_address == Py_None))
461         address = NULL;
462     else {
463         if (PyLong_Check(py_address))
464             address = PyLong_AsVoidPtr(py_address);
465         else {
466             PyErr_SetString(PyExc_TypeError, "address must be a long");
467             goto error_return;
468         }
469     }
470 
471     return shm_attach(self, address, flags);
472 
473     error_return:
474     return NULL;
475 }
476 
477 
478 PyObject *
SharedMemory_detach(SharedMemory * self)479 SharedMemory_detach(SharedMemory *self) {
480     if (-1 == shmdt(self->address)) {
481         self->address = NULL;
482         switch (errno) {
483             case EINVAL:
484                 PyErr_SetNone(pNotAttachedException);
485             break;
486 
487             default:
488                 PyErr_SetFromErrno(PyExc_OSError);
489             break;
490         }
491         goto error_return;
492     }
493 
494     self->address = NULL;
495 
496     Py_RETURN_NONE;
497 
498     error_return:
499     return NULL;
500 }
501 
502 
503 PyObject *
SharedMemory_read(SharedMemory * self,PyObject * args,PyObject * keywords)504 SharedMemory_read(SharedMemory *self, PyObject *args, PyObject *keywords) {
505     /* Tricky business here. A memory segment's size is a size_t which is
506        ulong or smaller. However, the largest string that Python can
507        construct is of ssize_t which is long or smaller. Therefore, the
508        size and offset variables must be ulongs while the byte_count
509        must be a long (and must not exceed LONG_MAX).
510        Mind your math!
511     */
512     long byte_count = 0;
513     unsigned long offset = 0;
514     unsigned long size;
515     PyObject *py_size;
516     char *keyword_list[ ] = {"byte_count", "offset", NULL};
517 
518     if (!PyArg_ParseTupleAndKeywords(args, keywords, "|lk", keyword_list,
519                                      &byte_count, &offset))
520         goto error_return;
521 
522     if (self->address == NULL) {
523         PyErr_SetString(pNotAttachedException,
524                         "Read attempt on unattached memory segment");
525         goto error_return;
526     }
527 
528     if ( (py_size = shm_get_value(self->id, SVIFP_SHM_SIZE)) ) {
529         size = PyLong_AsUnsignedLongMask(py_size);
530         Py_DECREF(py_size);
531     }
532     else
533         goto error_return;
534 
535     DPRINTF("offset = %lu, byte_count = %ld, size = %lu\n",
536             offset, byte_count, size);
537 
538     if (offset >= size) {
539         PyErr_SetString(PyExc_ValueError, "The offset must be less than the segment size");
540         goto error_return;
541     }
542 
543     if (byte_count < 0) {
544         PyErr_SetString(PyExc_ValueError, "The byte_count cannot be negative");
545         goto error_return;
546     }
547 
548     /* If the caller didn't specify a byte count or specified one that would
549        read past the end of the segment, return everything from the offset to
550        the end of the segment.
551        Be careful here not to express the second if condition w/addition, e.g.
552             (byte_count + offset > size)
553        It might be more intuitive but since byte_count is a long and offset
554        is a ulong, their sum could cause an arithmetic overflow. */
555     if ((!byte_count) || ((unsigned long)byte_count > size - offset)) {
556         // byte_count needs to be calculated
557         if (size - offset <= (unsigned long)PY_STRING_LENGTH_MAX)
558             byte_count = size - offset;
559         else {
560             // Caller is asking for more bytes than I can stuff into
561             // a Python string.
562             PyErr_Format(PyExc_ValueError,
563                          "The byte_count cannot exceed Python's max string length %ld",
564                          (long)PY_STRING_LENGTH_MAX);
565             goto error_return;
566         }
567     }
568 
569     return PyBytes_FromStringAndSize(self->address + offset, byte_count);
570 
571     error_return:
572     return NULL;
573 }
574 
575 
576 PyObject *
SharedMemory_write(SharedMemory * self,PyObject * args,PyObject * kw)577 SharedMemory_write(SharedMemory *self, PyObject *args, PyObject *kw) {
578     /* See comments for read() regarding "size issues". Note that here
579        Python provides the byte_count so it can't be negative.
580 
581        In Python >= 2.5, the Python argument specifier 's#' expects a
582        py_ssize_t for its second parameter. A long is long enough. It might
583        be too big, though, on platforms where a long is larger than
584        py_ssize_t. Therefore I *must* initialize it to 0 so that whatever
585        Python doesn't write to is zeroed out.
586    */
587     unsigned long offset = 0;
588     unsigned long size;
589     PyObject *py_size;
590     char *keyword_list[ ] = {"s", "offset", NULL};
591     static char args_format[] = "s*|k";
592     Py_buffer data;
593 
594     if (!PyArg_ParseTupleAndKeywords(args, kw, args_format, keyword_list,
595                           &data,
596                           &offset))
597         goto error_return;
598 
599     if (self->read_only) {
600         PyErr_SetString(PyExc_OSError, "Write attempt on read-only memory segment");
601         goto error_return;
602     }
603 
604     if (self->address == NULL) {
605         PyErr_SetString(pNotAttachedException, "Write attempt on unattached memory segment");
606         goto error_return;
607     }
608 
609     if ( (py_size = shm_get_value(self->id, SVIFP_SHM_SIZE)) ) {
610         size = PyLong_AsUnsignedLongMask(py_size);
611         Py_DECREF(py_size);
612     }
613     else
614         goto error_return;
615 
616     DPRINTF("write size check; size=%lu, offset=%lu, dat.len=%ld\n",
617             size, offset, data.len);
618 
619     // Remember that offset and size are both ulongs, so size > offset, then
620     // size - offset (as in the second part of the if expression) will evaluate
621     // to a "negative" number which is a very large ulong.
622     if ((offset > size) || ((unsigned long)data.len > size - offset)) {
623         PyErr_SetString(PyExc_ValueError, "Attempt to write past end of memory segment");
624         goto error_return;
625     }
626 
627     memcpy((self->address + offset), data.buf, data.len);
628 
629     PyBuffer_Release(&data);
630 
631     Py_RETURN_NONE;
632 
633     error_return:
634     PyBuffer_Release(&data);
635     return NULL;
636 }
637 
638 PyObject *
SharedMemory_remove(SharedMemory * self)639 SharedMemory_remove(SharedMemory *self) {
640     return shm_remove(self->id);
641 }
642 
643 
644 PyObject *
shm_get_key(SharedMemory * self)645 shm_get_key(SharedMemory *self) {
646     return KEY_T_TO_PY(self->key);
647 }
648 
649 PyObject *
shm_get_size(SharedMemory * self)650 shm_get_size(SharedMemory *self) {
651     return shm_get_value(self->id, SVIFP_SHM_SIZE);
652 }
653 
654 PyObject *
shm_get_address(SharedMemory * self)655 shm_get_address(SharedMemory *self) {
656     return PyLong_FromVoidPtr(self->address);
657 }
658 
659 PyObject *
shm_get_attached(SharedMemory * self)660 shm_get_attached(SharedMemory *self) {
661     if (self->address)
662         Py_RETURN_TRUE;
663     else
664         Py_RETURN_FALSE;
665 }
666 
667 PyObject *
shm_get_last_attach_time(SharedMemory * self)668 shm_get_last_attach_time(SharedMemory *self) {
669     return shm_get_value(self->id, SVIFP_SHM_LAST_ATTACH_TIME);
670 }
671 
672 PyObject *
shm_get_last_detach_time(SharedMemory * self)673 shm_get_last_detach_time(SharedMemory *self) {
674     return shm_get_value(self->id, SVIFP_SHM_LAST_DETACH_TIME);
675 }
676 
677 PyObject *
shm_get_last_change_time(SharedMemory * self)678 shm_get_last_change_time(SharedMemory *self) {
679     return shm_get_value(self->id, SVIFP_SHM_LAST_CHANGE_TIME);
680 }
681 
682 PyObject *
shm_get_creator_pid(SharedMemory * self)683 shm_get_creator_pid(SharedMemory *self) {
684     return shm_get_value(self->id, SVIFP_SHM_CREATOR_PID);
685 }
686 
687 PyObject *
shm_get_last_pid(SharedMemory * self)688 shm_get_last_pid(SharedMemory *self) {
689     return shm_get_value(self->id, SVIFP_SHM_LAST_AT_DT_PID);
690 }
691 
692 PyObject *
shm_get_number_attached(SharedMemory * self)693 shm_get_number_attached(SharedMemory *self) {
694     return shm_get_value(self->id, SVIFP_SHM_NUMBER_ATTACHED);
695 }
696 
697 PyObject *
shm_get_uid(SharedMemory * self)698 shm_get_uid(SharedMemory *self) {
699     return shm_get_value(self->id, SVIFP_IPC_PERM_UID);
700 }
701 
702 PyObject *
shm_get_cuid(SharedMemory * self)703 shm_get_cuid(SharedMemory *self) {
704     return shm_get_value(self->id, SVIFP_IPC_PERM_CUID);
705 }
706 
707 PyObject *
shm_get_cgid(SharedMemory * self)708 shm_get_cgid(SharedMemory *self) {
709     return shm_get_value(self->id, SVIFP_IPC_PERM_CGID);
710 }
711 
712 PyObject *
shm_get_mode(SharedMemory * self)713 shm_get_mode(SharedMemory *self) {
714     return shm_get_value(self->id, SVIFP_IPC_PERM_MODE);
715 }
716 
717 int
shm_set_uid(SharedMemory * self,PyObject * py_value)718 shm_set_uid(SharedMemory *self, PyObject *py_value) {
719     union ipc_perm_value new_value;
720 
721     if (!PyLong_Check(py_value))
722     {
723         PyErr_SetString(PyExc_TypeError, "Attribute 'uid' must be an integer");
724         goto error_return;
725     }
726 
727     new_value.uid = PyLong_AsLong(py_value);
728 
729     if (((uid_t)-1 == new_value.uid) && PyErr_Occurred()) {
730         // no idea what could have gone wrong -- punt it up to the caller
731         goto error_return;
732     }
733 
734     return shm_set_ipc_perm_value(self->id, SVIFP_IPC_PERM_UID, new_value);
735 
736     error_return:
737     return -1;
738 }
739 
740 
741 PyObject *
shm_get_gid(SharedMemory * self)742 shm_get_gid(SharedMemory *self) {
743     return shm_get_value(self->id, SVIFP_IPC_PERM_GID);
744 }
745 
746 int
shm_set_gid(SharedMemory * self,PyObject * py_value)747 shm_set_gid(SharedMemory *self, PyObject *py_value) {
748     union ipc_perm_value new_value;
749 
750     if (!PyLong_Check(py_value))
751     {
752         PyErr_Format(PyExc_TypeError, "attribute 'gid' must be an integer");
753         goto error_return;
754     }
755 
756     new_value.gid = PyLong_AsLong(py_value);
757 
758     if (((gid_t)-1 == new_value.gid) && PyErr_Occurred()) {
759         // no idea what could have gone wrong -- punt it up to the caller
760         goto error_return;
761     }
762 
763     return shm_set_ipc_perm_value(self->id, SVIFP_IPC_PERM_GID, new_value);
764 
765     error_return:
766     return -1;
767 }
768 
769 int
shm_set_mode(SharedMemory * self,PyObject * py_value)770 shm_set_mode(SharedMemory *self, PyObject *py_value) {
771     union ipc_perm_value new_value;
772 
773     if (!PyLong_Check(py_value))
774     {
775         PyErr_Format(PyExc_TypeError, "attribute 'mode' must be an integer");
776         goto error_return;
777     }
778 
779     new_value.mode = PyLong_AsLong(py_value);
780 
781     if (((mode_t)-1 == new_value.mode) && PyErr_Occurred()) {
782         // no idea what could have gone wrong -- punt it up to the caller
783         goto error_return;
784     }
785 
786     return shm_set_ipc_perm_value(self->id, SVIFP_IPC_PERM_MODE, new_value);
787 
788     error_return:
789     return -1;
790 }
791 
792