1 #define PY_SSIZE_T_CLEAN
2 #include "Python.h"
3 #include "structmember.h"
4 
5 #include "common.h"
6 #include "semaphore.h"
7 
8 #define ONE_BILLION 1000000000
9 
10 // This enum has to start at zero because its values are used as an
11 // arry index in sem_perform_semop().
12 enum SEMOP_TYPE {
13     SEMOP_P = 0,
14     SEMOP_V,
15     SEMOP_Z
16 };
17 
18 /* Struct to contain a timeout which can be None */
19 typedef struct {
20     int is_none;
21     int is_zero;
22     struct timespec timestamp;
23 } NoneableTimeout;
24 
25 
26 // It is recommended practice to define this union here in the .c module, but
27 // it's been common practice for platforms to define it themselves in header
28 // files. For instance, BSD and OS X do so (provisionally) in sem.h. As a
29 // result, I need to surround this with an #ifdef.
30 #ifdef _SEM_SEMUN_UNDEFINED
31 union semun {
32     int val;                    /* used for SETVAL only */
33     struct semid_ds *buf;       /* for IPC_STAT and IPC_SET */
34     unsigned short *array;      /* used for GETALL and SETALL */
35 #ifdef __linux__
36 	struct seminfo  *__buf;  	/* Buffer for IPC_INFO (Linux-specific) */
37 #endif
38 };
39 #endif
40 
41 
42 static int
convert_timeout(PyObject * py_timeout,void * converted_timeout)43 convert_timeout(PyObject *py_timeout, void *converted_timeout) {
44     // Converts a PyObject into a timeout if possible. The PyObject should
45     // be None or some sort of numeric value (e.g. int, float, etc.)
46     // converted_timeout should point to a NoneableTimeout. When this function
47     // returns, if the NoneableTimeout's is_none is true, then the rest of the
48     // struct is undefined. Otherwise, the rest of the struct is populated.
49     int rc = 0;
50     double simple_timeout = 0;
51     NoneableTimeout *p_timeout = (NoneableTimeout *)converted_timeout;
52 
53     // The timeout can be None or any Python numeric type (float,
54     // int, long).
55     if (py_timeout == Py_None)
56         rc = 1;
57     else if (PyFloat_Check(py_timeout)) {
58         rc = 1;
59         simple_timeout = PyFloat_AsDouble(py_timeout);
60     }
61     else if (PyLong_Check(py_timeout)) {
62         rc = 1;
63         simple_timeout = (double)PyLong_AsLong(py_timeout);
64     }
65 
66     // The timeout may not be negative.
67     if ((rc) && (simple_timeout < 0))
68         rc = 0;
69 
70     if (!rc)
71         PyErr_SetString(PyExc_TypeError,
72                         "The timeout must be None or a non-negative number");
73     else {
74         if (py_timeout == Py_None)
75             p_timeout->is_none = 1;
76         else {
77             p_timeout->is_none = 0;
78 
79             p_timeout->is_zero = (!simple_timeout);
80 
81             // Note the difference between this and POSIX timeouts. System V
82             // timeouts expect tv_sec to represent a delta from the current
83             // time whereas POSIX semaphores expect an absolute value.
84             p_timeout->timestamp.tv_sec = (time_t)floor(simple_timeout);
85             p_timeout->timestamp.tv_nsec = (long)((simple_timeout - floor(simple_timeout)) * ONE_BILLION);
86         }
87     }
88 
89     return rc;
90 }
91 
92 
93 PyObject *
sem_str(Semaphore * self)94 sem_str(Semaphore *self) {
95     return PyUnicode_FromFormat("Key=%ld, id=%d", (long)self->key, self->id);
96 }
97 
98 
99 PyObject *
sem_repr(Semaphore * self)100 sem_repr(Semaphore *self) {
101     return PyUnicode_FromFormat("sysv_ipc.Semaphore(%ld)", (long)self->key);
102 }
103 
104 
105 static void
sem_set_error(void)106 sem_set_error(void) {
107     switch (errno) {
108         case ENOENT:
109         case EINVAL:
110             PyErr_SetString(pExistentialException,
111                                 "No semaphore exists with the specified key");
112         break;
113 
114         case EEXIST:
115             PyErr_SetString(pExistentialException,
116                         "A semaphore with the specified key already exists");
117         break;
118 
119         case EACCES:
120             PyErr_SetString(pPermissionsException, "Permission denied");
121         break;
122 
123         case ERANGE:
124             PyErr_Format(PyExc_ValueError,
125                 "The semaphore's value must remain between 0 and %ld (SEMAPHORE_VALUE_MAX)",
126                 (long)SEMAPHORE_VALUE_MAX);
127         break;
128 
129         case EAGAIN:
130             PyErr_SetString(pBusyException, "The semaphore is busy");
131         break;
132 
133         case EIDRM:
134             PyErr_SetString(pExistentialException, "The semaphore was removed");
135         break;
136 
137         case EINTR:
138             PyErr_SetString(pBaseException, "Signaled while waiting");
139         break;
140 
141         case ENOMEM:
142             PyErr_SetString(PyExc_MemoryError, "Not enough memory");
143         break;
144 
145         default:
146             PyErr_SetFromErrno(PyExc_OSError);
147         break;
148     }
149 }
150 
151 
152 static PyObject *
sem_perform_semop(enum SEMOP_TYPE op_type,Semaphore * self,PyObject * args,PyObject * keywords)153 sem_perform_semop(enum SEMOP_TYPE op_type, Semaphore *self, PyObject *args, PyObject *keywords) {
154     int rc = 0;
155     NoneableTimeout timeout;
156     struct sembuf op[1];
157     /* delta (a.k.a. struct sembuf.sem_op) is a short
158        ref: http://www.opengroup.org/onlinepubs/000095399/functions/semop.html
159     */
160     short int delta;
161     char *keyword_list[3][3] = {
162                     {"timeout", "delta", NULL},     // P == acquire
163                     {"delta", NULL},                // V == release
164                     {"timeout", NULL}               // Z == zero test
165                 };
166 
167 
168     /* Initialize this to the default value. If the user doesn't pass a
169        timeout, Python won't call convert_timeout() and so the timeout
170        will be otherwise uninitialized.
171     */
172     timeout.is_none = 1;
173 
174     /* op_type is P, V or Z corresponding to the 3 Semaphore methods
175        that call that call semop(). */
176     switch (op_type) {
177         case SEMOP_P:
178             // P == acquire
179             delta = -1;
180             rc = PyArg_ParseTupleAndKeywords(args, keywords, "|O&h",
181                                              keyword_list[SEMOP_P],
182                                              convert_timeout, &timeout,
183                                              &delta);
184 
185             if (rc && !delta) {
186                 rc = 0;
187                 PyErr_SetString(PyExc_ValueError, "The delta must be non-zero");
188             }
189             else
190                 delta = -abs(delta);
191         break;
192 
193         case SEMOP_V:
194             // V == release
195             delta = 1;
196             rc = PyArg_ParseTupleAndKeywords(args, keywords, "|h",
197                                              keyword_list[SEMOP_V],
198                                              &delta);
199 
200             if (rc && !delta) {
201                 rc = 0;
202                 PyErr_SetString(PyExc_ValueError, "The delta must be non-zero");
203             }
204             else
205                 delta = abs(delta);
206         break;
207 
208         case SEMOP_Z:
209             // Z = Zero test
210             delta = 0;
211             rc = PyArg_ParseTupleAndKeywords(args, keywords, "|O&",
212                                              keyword_list[SEMOP_Z],
213                                              convert_timeout, &timeout);
214         break;
215 
216         default:
217             PyErr_Format(pInternalException, "Bad op_type (%d)", op_type);
218             rc = 0;
219         break;
220     }
221 
222     if (!rc)
223         goto error_return;
224 
225     // Now that the caller's params have been vetted, I set up the op struct
226     // that I'm going to pass to semop().
227     op[0].sem_num = 0;
228     op[0].sem_op = delta;
229     op[0].sem_flg = self->op_flags;
230 
231     Py_BEGIN_ALLOW_THREADS;
232 #ifdef SEMTIMEDOP_EXISTS
233     // Call semtimedop() if appropriate, otherwise call semop()
234     if (!timeout.is_none) {
235         DPRINTF("calling semtimedop on id %d, op.sem_op=%d, op.flags=0x%x\n",
236                 self->id, op[0].sem_op, op[0].sem_flg);
237         DPRINTF("timeout tv_sec = %ld; timeout tv_nsec = %ld\n",
238                 timeout.timestamp.tv_sec, timeout.timestamp.tv_nsec);
239         rc = semtimedop(self->id, op, 1, &timeout.timestamp);
240     }
241     else {
242         DPRINTF("calling semop on id %d, op.sem_op = %d, op.flags=%x\n",
243                 self->id, op[0].sem_op, op[0].sem_flg);
244         rc = semop(self->id, op, 1);
245     }
246 #else
247     // no support for semtimedop(), always call semop() instead.
248     DPRINTF("calling semop on id %d, op.sem_op = %d, op.flags=%x\n",
249             self->id, op[0].sem_op, op[0].sem_flg);
250     rc = semop(self->id, op, 1);
251 #endif
252     Py_END_ALLOW_THREADS;
253 
254     if (rc == -1) {
255         sem_set_error();
256         goto error_return;
257     }
258 
259     Py_RETURN_NONE;
260 
261     error_return:
262     return NULL;
263 }
264 
265 
266 // cmd can be any of the values defined in the documentation for semctl().
267 static PyObject *
sem_get_semctl_value(int semaphore_id,int cmd)268 sem_get_semctl_value(int semaphore_id, int cmd) {
269     int rc;
270 
271     // semctl() returns an int
272     // ref: http://www.opengroup.org/onlinepubs/000095399/functions/semctl.html
273     rc = semctl(semaphore_id, 0, cmd);
274 
275     if (-1 == rc) {
276         sem_set_error();
277         goto error_return;
278     }
279 
280     return PyLong_FromLong(rc);
281 
282     error_return:
283     return NULL;
284 }
285 
286 
287 static PyObject *
sem_get_ipc_perm_value(int id,enum GET_SET_IDENTIFIERS field)288 sem_get_ipc_perm_value(int id, enum GET_SET_IDENTIFIERS field) {
289     struct semid_ds sem_info;
290     union semun arg;
291     PyObject *py_value = NULL;
292 
293     arg.buf = &sem_info;
294 
295     // Here I get the values currently associated with the semaphore.
296     if (-1 == semctl(id, 0, IPC_STAT, arg)) {
297         sem_set_error();
298         goto error_return;
299     }
300 
301     switch (field) {
302         case SVIFP_IPC_PERM_UID:
303             py_value = UID_T_TO_PY(sem_info.sem_perm.uid);
304         break;
305 
306         case SVIFP_IPC_PERM_GID:
307             py_value = GID_T_TO_PY(sem_info.sem_perm.gid);
308         break;
309 
310         case SVIFP_IPC_PERM_CUID:
311             py_value = UID_T_TO_PY(sem_info.sem_perm.cuid);
312         break;
313 
314         case SVIFP_IPC_PERM_CGID:
315             py_value = GID_T_TO_PY(sem_info.sem_perm.cgid);
316         break;
317 
318         case SVIFP_IPC_PERM_MODE:
319             py_value = MODE_T_TO_PY(sem_info.sem_perm.mode);
320         break;
321 
322         // This isn't an ipc_perm value but it fits here anyway.
323         case SVIFP_SEM_OTIME:
324             py_value = TIME_T_TO_PY(sem_info.sem_otime);
325         break;
326 
327         default:
328             PyErr_Format(pInternalException,
329                 "Bad field %d passed to sem_get_ipc_perm_value", field);
330             goto error_return;
331         break;
332     }
333 
334     return py_value;
335 
336     error_return:
337     return NULL;
338 }
339 
340 
341 static int
sem_set_ipc_perm_value(int id,enum GET_SET_IDENTIFIERS field,PyObject * py_value)342 sem_set_ipc_perm_value(int id, enum GET_SET_IDENTIFIERS field, PyObject *py_value) {
343     struct semid_ds sem_info;
344     union semun arg;
345 
346     arg.buf = &sem_info;
347 
348     if (!PyLong_Check(py_value))
349     {
350         PyErr_Format(PyExc_TypeError, "The attribute must be an integer");
351         goto error_return;
352     }
353 
354     arg.buf = &sem_info;
355 
356     /* Here I get the current values associated with the semaphore. It's
357        critical to populate sem_info with current values here (rather than
358        just using the struct filled with whatever garbage it acquired from
359        being declared on the stack) because the call to semctl(...IPC_SET...)
360        below will copy uid, gid and mode to the kernel's data structure.
361     */
362     if (-1 == semctl(id, 0, IPC_STAT, arg)) {
363         sem_set_error();
364         goto error_return;
365     }
366 
367     // Below I'm stuffing a Python int converted to a C long into a
368     // uid_t, gid_t or mode_t. A long might not fit, hence the explicit
369     // cast. If the user passes a value that's too big, tough cookies.
370     switch (field) {
371         case SVIFP_IPC_PERM_UID:
372             sem_info.sem_perm.uid = (uid_t)PyLong_AsLong(py_value);
373         break;
374 
375         case SVIFP_IPC_PERM_GID:
376             sem_info.sem_perm.gid = (gid_t)PyLong_AsLong(py_value);
377         break;
378 
379         case SVIFP_IPC_PERM_MODE:
380             sem_info.sem_perm.mode = (mode_t)PyLong_AsLong(py_value);
381         break;
382 
383         default:
384             PyErr_Format(pInternalException,
385                 "Bad field %d passed to sem_set_ipc_perm_value", field);
386             goto error_return;
387         break;
388     }
389 
390     if (-1 == semctl(id, 0, IPC_SET, arg)) {
391         sem_set_error();
392         goto error_return;
393     }
394 
395     return 0;
396 
397     error_return:
398     return -1;
399 }
400 
401 
402 PyObject *
sem_remove(int id)403 sem_remove(int id) {
404     if (NULL == sem_get_semctl_value(id, IPC_RMID))
405         return NULL;
406     else
407         Py_RETURN_NONE;
408 }
409 
410 
411 void
Semaphore_dealloc(Semaphore * self)412 Semaphore_dealloc(Semaphore *self) {
413     Py_TYPE(self)->tp_free((PyObject*)self);
414 }
415 
416 PyObject *
Semaphore_new(PyTypeObject * type,PyObject * args,PyObject * keywords)417 Semaphore_new(PyTypeObject *type, PyObject *args, PyObject *keywords) {
418     Semaphore *self;
419 
420     self = (Semaphore *)type->tp_alloc(type, 0);
421 
422     return (PyObject *)self;
423 }
424 
425 
426 int
Semaphore_init(Semaphore * self,PyObject * args,PyObject * keywords)427 Semaphore_init(Semaphore *self, PyObject *args, PyObject *keywords) {
428     int mode = 0600;
429     int initial_value = 0;
430     int flags = 0;
431     union semun arg;
432     char *keyword_list[ ] = {"key", "flags", "mode", "initial_value", NULL};
433     NoneableKey key;
434 
435     //Semaphore(key, [flags = 0, [mode = 0600, [initial_value = 0]]])
436 
437     if (!PyArg_ParseTupleAndKeywords(args, keywords, "O&|iii", keyword_list,
438                                      &convert_key_param, &key, &flags,
439                                      &mode, &initial_value))
440         goto error_return;
441 
442     DPRINTF("key is none = %d, key value = %ld\n", key.is_none, (long)key.value);
443 
444     if ( !(flags & IPC_CREAT) && (flags & IPC_EXCL) ) {
445 		PyErr_SetString(PyExc_ValueError,
446                 "IPC_EXCL must be combined with IPC_CREAT");
447         goto error_return;
448     }
449 
450     if (key.is_none && ((flags & IPC_EXCL) != IPC_EXCL)) {
451 		PyErr_SetString(PyExc_ValueError,
452                 "Key can only be None if IPC_EXCL is set");
453         goto error_return;
454     }
455 
456     self->op_flags = 0;
457 
458     // I mask the caller's flags against the two IPC_* flags to ensure that
459     // nothing funky sneaks into the flags.
460     flags &= (IPC_CREAT | IPC_EXCL);
461 
462     // Note that Sys V sems can be in "sets" (arrays) but I hardcode this
463     // to always be a set with just one member.
464     // Permissions and flags (i.e. IPC_CREAT | IPC_EXCL) are both crammed
465     // into the 3rd param.
466     if (key.is_none) {
467         // (key == None) ==> generate a key for the caller
468         do {
469             errno = 0;
470             self->key = get_random_key();
471 
472             DPRINTF("Calling semget, key=%ld, mode=%o, flags=%x\n",
473                         (long)self->key, mode, flags);
474             self->id = semget(self->key, 1, mode | flags);
475         } while ( (-1 == self->id) && (EEXIST == errno) );
476     }
477     else {
478         // (key != None) ==> use key supplied by the caller
479         self->key = key.value;
480 
481         DPRINTF("Calling semget, key=%ld, mode=%o, flags=%x\n",
482                     (long)self->key, mode, flags);
483         self->id = semget(self->key, 1, mode | flags);
484     }
485 
486     DPRINTF("id == %d\n", self->id);
487 
488     if (self->id == -1) {
489         sem_set_error();
490         goto error_return;
491     }
492 
493     // Before attempting to set the initial value, I have to be sure that
494     // I created this semaphore and that I have write access to it.
495     if ((flags & IPC_CREX) && (mode & 0200)) {
496         DPRINTF("setting initial value to %d\n", initial_value);
497         arg.val = initial_value;
498 
499         if (-1 == semctl(self->id, 0, SETVAL, arg)) {
500             sem_set_error();
501             goto error_return;
502         }
503     }
504 
505     return 0;
506 
507     error_return:
508     return -1;
509 }
510 
511 
512 PyObject *
Semaphore_P(Semaphore * self,PyObject * args,PyObject * keywords)513 Semaphore_P(Semaphore *self, PyObject *args, PyObject *keywords) {
514     return sem_perform_semop(SEMOP_P, self, args, keywords);
515 }
516 
517 
518 PyObject *
Semaphore_acquire(Semaphore * self,PyObject * args,PyObject * keywords)519 Semaphore_acquire(Semaphore *self, PyObject *args, PyObject *keywords) {
520     return Semaphore_P(self, args, keywords);
521 }
522 
523 
524 PyObject *
Semaphore_V(Semaphore * self,PyObject * args,PyObject * keywords)525 Semaphore_V(Semaphore *self, PyObject *args, PyObject *keywords) {
526     return sem_perform_semop(SEMOP_V, self, args, keywords);
527 }
528 
529 
530 PyObject *
Semaphore_release(Semaphore * self,PyObject * args,PyObject * keywords)531 Semaphore_release(Semaphore *self, PyObject *args, PyObject *keywords) {
532     return Semaphore_V(self, args, keywords);
533 }
534 
535 
536 PyObject *
Semaphore_Z(Semaphore * self,PyObject * args,PyObject * keywords)537 Semaphore_Z(Semaphore *self, PyObject *args, PyObject *keywords) {
538     return sem_perform_semop(SEMOP_Z, self, args, keywords);
539 }
540 
541 
542 PyObject *
Semaphore_remove(Semaphore * self)543 Semaphore_remove(Semaphore *self) {
544     return sem_remove(self->id);
545 }
546 
547 PyObject *
Semaphore_enter(Semaphore * self)548 Semaphore_enter(Semaphore *self) {
549     PyObject *args = PyTuple_New(0);
550     PyObject *retval = NULL;
551 
552     if (Semaphore_acquire(self, args, NULL)) {
553         retval = (PyObject *)self;
554         Py_INCREF(self);
555     }
556 
557     Py_DECREF(args);
558 
559     return retval;
560 }
561 
562 PyObject *
Semaphore_exit(Semaphore * self,PyObject * args)563 Semaphore_exit(Semaphore *self, PyObject *args) {
564     PyObject *release_args = PyTuple_New(0);
565     PyObject *retval = NULL;
566 
567     DPRINTF("exiting context and releasing semaphore %ld\n", (long)self->key);
568 
569     retval = Semaphore_release(self, release_args, NULL);
570 
571     Py_DECREF(release_args);
572 
573     return retval;
574 }
575 
576 PyObject *
sem_get_key(Semaphore * self)577 sem_get_key(Semaphore *self) {
578     return KEY_T_TO_PY(self->key);
579 }
580 
581 PyObject *
sem_get_value(Semaphore * self)582 sem_get_value(Semaphore *self) {
583     return sem_get_semctl_value(self->id, GETVAL);
584 }
585 
586 
587 int
sem_set_value(Semaphore * self,PyObject * py_value)588 sem_set_value(Semaphore *self, PyObject *py_value)
589 {
590     union semun arg;
591     long value;
592 
593     if (!PyLong_Check(py_value))
594     {
595 		PyErr_Format(PyExc_TypeError, "Attribute 'value' must be an integer");
596         goto error_return;
597     }
598 
599     value = PyLong_AsLong(py_value);
600 
601     DPRINTF("C value is %ld\n", value);
602 
603     if ((-1 == value) && PyErr_Occurred()) {
604         // No idea wht could cause this -- just raise it to the caller.
605         goto error_return;
606     }
607 
608     if ((value < 0) || (value > SEMAPHORE_VALUE_MAX)) {
609 		PyErr_Format(PyExc_ValueError,
610 		    "Attribute 'value' must be between 0 and %ld (SEMAPHORE_VALUE_MAX)",
611 		    (long)SEMAPHORE_VALUE_MAX);
612         goto error_return;
613     }
614 
615     arg.val = value;
616 
617     if (-1 == semctl(self->id, 0, SETVAL, arg)) {
618         sem_set_error();
619         goto error_return;
620     }
621 
622     return 0;
623 
624     error_return:
625     return -1;
626 }
627 
628 
629 PyObject *
sem_get_block(Semaphore * self)630 sem_get_block(Semaphore *self) {
631     DPRINTF("op_flags: %x\n", self->op_flags);
632     return PyBool_FromLong( (self->op_flags & IPC_NOWAIT) ? 0 : 1);
633 }
634 
635 
636 int
sem_set_block(Semaphore * self,PyObject * py_value)637 sem_set_block(Semaphore *self, PyObject *py_value)
638 {
639     DPRINTF("op_flags before: %x\n", self->op_flags);
640 
641     if (PyObject_IsTrue(py_value))
642         self->op_flags &= ~IPC_NOWAIT;
643     else
644         self->op_flags |= IPC_NOWAIT;
645 
646     DPRINTF("op_flags after: %x\n", self->op_flags);
647 
648     return 0;
649 }
650 
651 
652 PyObject *
sem_get_mode(Semaphore * self)653 sem_get_mode(Semaphore *self) {
654     return sem_get_ipc_perm_value(self->id, SVIFP_IPC_PERM_MODE);
655 }
656 
657 
658 int
sem_set_mode(Semaphore * self,PyObject * py_value)659 sem_set_mode(Semaphore *self, PyObject *py_value) {
660     return sem_set_ipc_perm_value(self->id, SVIFP_IPC_PERM_MODE, py_value);
661 }
662 
663 
664 PyObject *
sem_get_undo(Semaphore * self)665 sem_get_undo(Semaphore *self) {
666     return PyBool_FromLong( (self->op_flags & SEM_UNDO) ? 1 : 0 );
667 }
668 
669 
670 int
sem_set_undo(Semaphore * self,PyObject * py_value)671 sem_set_undo(Semaphore *self, PyObject *py_value)
672 {
673     DPRINTF("op_flags before: %x\n", self->op_flags);
674 
675     if (PyObject_IsTrue(py_value))
676         self->op_flags |= SEM_UNDO;
677     else
678         self->op_flags &= ~SEM_UNDO;
679 
680     DPRINTF("op_flags after: %x\n", self->op_flags);
681 
682     return 0;
683 }
684 
685 
686 PyObject *
sem_get_uid(Semaphore * self)687 sem_get_uid(Semaphore *self) {
688     return sem_get_ipc_perm_value(self->id, SVIFP_IPC_PERM_UID);
689 }
690 
691 
692 int
sem_set_uid(Semaphore * self,PyObject * py_value)693 sem_set_uid(Semaphore *self, PyObject *py_value) {
694     return sem_set_ipc_perm_value(self->id, SVIFP_IPC_PERM_UID, py_value);
695 }
696 
697 
698 PyObject *
sem_get_gid(Semaphore * self)699 sem_get_gid(Semaphore *self) {
700     return sem_get_ipc_perm_value(self->id, SVIFP_IPC_PERM_GID);
701 }
702 
703 
704 int
sem_set_gid(Semaphore * self,PyObject * py_value)705 sem_set_gid(Semaphore *self, PyObject *py_value) {
706     return sem_set_ipc_perm_value(self->id, SVIFP_IPC_PERM_GID, py_value);
707 }
708 
709 PyObject *
sem_get_c_uid(Semaphore * self)710 sem_get_c_uid(Semaphore *self) {
711     return sem_get_ipc_perm_value(self->id, SVIFP_IPC_PERM_CUID);
712 }
713 
714 PyObject *
sem_get_c_gid(Semaphore * self)715 sem_get_c_gid(Semaphore *self) {
716     return sem_get_ipc_perm_value(self->id, SVIFP_IPC_PERM_CGID);
717 }
718 
719 PyObject *
sem_get_last_pid(Semaphore * self)720 sem_get_last_pid(Semaphore *self) {
721     return sem_get_semctl_value(self->id, GETPID);
722 }
723 
724 PyObject *
sem_get_waiting_for_nonzero(Semaphore * self)725 sem_get_waiting_for_nonzero(Semaphore *self) {
726     return sem_get_semctl_value(self->id, GETNCNT);
727 }
728 
729 PyObject *
sem_get_waiting_for_zero(Semaphore * self)730 sem_get_waiting_for_zero(Semaphore *self) {
731     return sem_get_semctl_value(self->id, GETZCNT);
732 }
733 
734 PyObject *
sem_get_o_time(Semaphore * self)735 sem_get_o_time(Semaphore *self) {
736     return sem_get_ipc_perm_value(self->id, SVIFP_SEM_OTIME);
737 }
738 
739