/*
 * SystemVIPC: SystemV IPC support for Ruby
 * 
 * $Source: /var/cvs/sysvipc/sysvipc/sysvipc.c,v $
 *
 * $Revision: 1.35 $
 * $Date: 2007/07/24 05:30:56 $
 *
 * Copyright (C) 2001, 2006, 2007  Daiki Ueno
 * Copyright (C) 2006, 2007  James Steven Jenkins
 * 
 * SysVIPC is copyrighted free software by Daiki Ueno, Steven Jenkins,
 * and others.  You can redistribute it and/or modify it under either
 * the terms of the GNU General Public License Version 2 (see file 'GPL'),
 * or the conditions below:
 * 
 *   1. You may make and give away verbatim copies of the source form of the
 *      software without restriction, provided that you duplicate all of the
 *      original copyright notices and associated disclaimers.
 * 
 *   2. You may modify your copy of the software in any way, provided that
 *      you do at least ONE of the following:
 * 
 *        a) place your modifications in the Public Domain or otherwise
 *           make them Freely Available, such as by posting said
 * 	  modifications to Usenet or an equivalent medium, or by allowing
 * 	  the author to include your modifications in the software.
 * 
 *        b) use the modified software only within your corporation or
 *           organization.
 * 
 *        c) rename any non-standard executables so the names do not conflict
 * 	  with standard executables, which must also be provided.
 * 
 *        d) make other distribution arrangements with the author.
 * 
 *   3. You may distribute the software in object code or executable
 *      form, provided that you do at least ONE of the following:
 * 
 *        a) distribute the executables and library files of the software,
 * 	  together with instructions (in the manual page or equivalent)
 * 	  on where to get the original distribution.
 * 
 *        b) accompany the distribution with the machine-readable source of
 * 	  the software.
 * 
 *        c) give non-standard executables non-standard names, with
 *           instructions on where to get the original software distribution.
 * 
 *        d) make other distribution arrangements with the author.
 * 
 *   4. You may modify and include the part of the software into any other
 *      software (possibly commercial).  
 * 
 *   5. The scripts and library files supplied as input to or produced as 
 *      output from the software do not automatically fall under the
 *      copyright of the software, but belong to whomever generated them, 
 *      and may be sold commercially, and may be aggregated with this
 *      software.
 * 
 *   6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
 *      IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 *      WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 *      PURPOSE.
 */

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include <errno.h>
#include "ruby.h"
#include "rubysig.h"

#ifndef EWOULDBLOCK
#define EWOULDBLOCK EAGAIN
#endif

struct ipcid_ds {
  int id;
  int flags;
  union {
    struct msqid_ds msgstat;
    struct semid_ds semstat;
    struct shmid_ds shmstat;
  } u;

#define msgstat u.msgstat
#define semstat u.semstat
#define shmstat u.shmstat

  void (*stat) (struct ipcid_ds *);
  void (*rmid) (struct ipcid_ds *);
  struct ipc_perm * (*perm) (struct ipcid_ds *);

  void *data;
};

#if !defined(HAVE_TYPE_STRUCT_MSGBUF)
struct msgbuf {
  long mtype;
  char mtext[1];
};
#endif

#if !defined(HAVE_TYPE_UNION_SEMUN)
union semun {
  int val;                    /* value for SETVAL */
  struct semid_ds *buf;       /* buffer for IPC_STAT, IPC_SET */
  unsigned short int *array;  /* array for GETALL, SETALL */
  struct seminfo *__buf;      /* buffer for IPC_INFO */
};
#endif

static VALUE cError;

/*
 * call-seq:
 *   SystemVIPC.ftok(pathname, proj_id) -> Fixnum
 *
 * Convert a pathname and a project identifier to a System V IPC
 * key. +pathname+ is a string filename and +proj_id+ is an integer.
 * See ftok(3).
 */

static VALUE
rb_ftok (klass, v_path, v_id)
     VALUE klass, v_path, v_id;
{
  const char *path = STR2CSTR (v_path);
  key_t key;

  key = ftok (path, NUM2INT (v_id) & 0x7f);
  if (key == -1)
    rb_sys_fail ("ftok(2)");

  return INT2FIX (key);
}

static struct ipcid_ds *
get_ipcid (obj)
     VALUE obj;
{
  struct ipcid_ds *ipcid;
  Data_Get_Struct (obj, struct ipcid_ds, ipcid);

  if (ipcid->id < 0)
    rb_raise (cError, "closed handle");
  return ipcid;
}

static struct ipcid_ds *
get_ipcid_and_stat (obj)
     VALUE obj;
{
  struct ipcid_ds *ipcid;
  ipcid = get_ipcid (obj);
  ipcid->stat (ipcid);
  return ipcid;
}

/* call-seq:
 *   remove -> IPCObject
 *
 * Remove the IPCObject. Return self.
 */

static VALUE
rb_ipc_remove (obj)
     VALUE obj;
{
  struct ipcid_ds *ipcid;

  ipcid = get_ipcid (obj);
  ipcid->rmid (ipcid);

  return obj;
}

static void
msg_stat (msgid)
     struct ipcid_ds *msgid;
{
  if (msgctl (msgid->id, IPC_STAT, &msgid->msgstat) == -1)
    rb_sys_fail ("msgctl(2)");
}

static struct ipc_perm *
msg_perm (msgid)
     struct ipcid_ds *msgid;
{
  return &msgid->msgstat.msg_perm;
}

static void
msg_rmid (msgid)
     struct ipcid_ds *msgid;
{
  if (msgid->id < 0)
    rb_raise (cError, "already removed");
  if (msgctl (msgid->id, IPC_RMID, 0) == -1)
    rb_sys_fail ("msgctl(2)");
  msgid->id = -1;
}

/*
 * call-seq:
 *   MessageQueue.new(key, msgflg = 0) -> MessageQueue
 *
 * Create a new MessageQueue object associated with the message
 * queue identified by +key+. +msgflg+ is a bitwise OR selected from
 * IPC_CREAT and IPC_EXCL. See msgget(2).
 */

static VALUE
rb_msg_s_new (argc, argv, klass)
     int argc;
     VALUE *argv, klass;
{
  struct ipcid_ds *msgid;
  VALUE dst, v_key, v_msgflg;

  dst = Data_Make_Struct (klass, struct ipcid_ds, NULL, free, msgid);
  rb_scan_args (argc, argv, "11", &v_key, &v_msgflg);
  if (!NIL_P (v_msgflg))
    msgid->flags = NUM2INT (v_msgflg);
  msgid->id = msgget ((key_t)NUM2INT (v_key), msgid->flags);
  if (msgid->id == -1)
    rb_sys_fail ("msgget(2)");
  msgid->stat = msg_stat;
  msgid->perm = msg_perm;
  msgid->rmid = msg_rmid;

  return dst;
}

/*
 * call-seq:
 *   send(mtype, mtext, msgflg = 0) ->  MessageQueue
 *
 * Send message +mtext+ of type +mtype+ with flags +msgflg+. Return
 * self.  See msgop(2).
 */

static VALUE
rb_msg_send (argc, argv, obj)
     int argc;
     VALUE *argv, obj;
{
  VALUE v_type, v_buf, v_flags;
  int flags = 0, error, nowait;
  struct msgbuf *msgp;
  struct ipcid_ds *msgid;
  char *buf;
  size_t len;

  rb_scan_args (argc, argv, "21", &v_type, &v_buf, &v_flags);
  if (!NIL_P (v_flags))
    flags = NUM2INT (v_flags);
  
  len = RSTRING (v_buf)->len;
  buf = RSTRING (v_buf)->ptr;

  msgp = (struct msgbuf *) ALLOC_N (char, sizeof (long) + len);
  msgp->mtype = NUM2LONG (v_type);
  memcpy (msgp->mtext, buf, len);

  msgid = get_ipcid (obj);

  nowait = flags & IPC_NOWAIT;
  if (!rb_thread_alone()) flags |= IPC_NOWAIT;

 retry:
  TRAP_BEG;
  error = msgsnd (msgid->id, msgp, len, flags);
  TRAP_END;
  if (error == -1)
    {
      switch (errno)
	{
	case EINTR:
	    goto retry;
	case EWOULDBLOCK:
#if EAGAIN != EWOULDBLOCK
	case EAGAIN:
#endif
	    if (!nowait)
	      {
		rb_thread_polling ();
		goto retry;
	      }
	}
      rb_sys_fail ("msgsnd(2)");
    }

  free(msgp);
  return obj;
}

/*
 * call-seq:
 *   recv(mtype, msgsz, msgflg = 0) ->  MessageQueue
 *
 * Receive up to +msgsz+ bytes of the next message of type +mtype+
 * with flags +msgflg+. Return self. See msgop(2).
 */

static VALUE
rb_msg_recv (argc, argv, obj)
     int argc;
     VALUE *argv, obj;
{
  VALUE v_type, v_len, v_flags;
  int flags = 0, nowait;
  struct msgbuf *msgp;
  struct ipcid_ds *msgid;
  long type;
  size_t rlen, len;
  VALUE ret;

  rb_scan_args (argc, argv, "21", &v_type, &v_len, &v_flags);
  type = NUM2LONG (v_type);
  len = NUM2INT (v_len);
  if (!NIL_P (v_flags))
    flags = NUM2INT (v_flags);

  msgp = (struct msgbuf *) ALLOC_N (char, sizeof (long) + len);
  msgid = get_ipcid (obj);

  nowait = flags & IPC_NOWAIT;
  if (!rb_thread_alone()) flags |= IPC_NOWAIT;

 retry:
  TRAP_BEG;
  rlen = msgrcv (msgid->id, msgp, len, type, flags);
  TRAP_END;
  if (rlen == (size_t)-1)
    {
      switch (errno)
	{
	case EINTR:
	    goto retry;
	case ENOMSG:
	case EWOULDBLOCK:
#if EAGAIN != EWOULDBLOCK
	case EAGAIN:
#endif
	    if (!nowait)
	      {
		rb_thread_polling ();
		goto retry;
	      }
	}
      rb_sys_fail ("msgrcv(2)");
    }

  ret = rb_str_new (msgp->mtext, rlen);
  free(msgp);
  return ret;
}

static void
sem_stat (semid)
     struct ipcid_ds *semid;
{
  union semun arg;

  arg.buf = &semid->semstat;
  if (semctl (semid->id, 0, IPC_STAT, arg) == -1)
    rb_sys_fail ("semctl(2)");
}

static struct ipc_perm *
sem_perm (semid)
     struct ipcid_ds *semid;
{
  return &semid->semstat.sem_perm;
}

static void
sem_rmid (semid)
     struct ipcid_ds *semid;
{
  if (semid->id < 0)
    rb_raise (cError, "already removed");
  if (semctl (semid->id, 0, IPC_RMID, 0) == -1)
    rb_sys_fail ("semctl(2)");
  semid->id = -1;
}

/*
 * call-seq:
 *   Semaphore.new(key, nsems, semflg = 0) -> Semaphore
 *
 * Create a new Semaphore object encapsulating the semaphore
 * set identified by +key+. +nsems+ is the number of semaphores
 * in the set, and +semflg+ is a bitwise OR selected from
 * IPC_CREAT and IPC_EXCL. See semget(2).
 */

static VALUE
rb_sem_s_new (argc, argv, klass)
     int argc;
     VALUE *argv, klass;
{
  struct ipcid_ds *semid;
  VALUE dst, v_key, v_nsems, v_semflg;
  int nsems = 0;

  dst = Data_Make_Struct (klass, struct ipcid_ds, NULL, free, semid);
  rb_scan_args (argc, argv, "12", &v_key, &v_nsems, &v_semflg);
  if (!NIL_P (v_nsems))
    nsems = NUM2INT (v_nsems);
  if (!NIL_P (v_semflg))
    semid->flags = NUM2INT (v_semflg);
  semid->id = semget ((key_t)NUM2INT (v_key), nsems, semid->flags);
  if (semid->id == -1)
    rb_sys_fail ("semget(2)");
  semid->stat = sem_stat;
  semid->perm = sem_perm;
  semid->rmid = sem_rmid;

  return dst;
}

#define Check_Valid_Semnum(n, semid)		\
  if (n > semid->semstat.sem_nsems)		\
    rb_raise (cError, "invalid semnum")

/*
 * call-seq:
 *   to_a -> Array
 *
 * Return values for the semaphore set as an array. See semctl(2).
 */

static VALUE
rb_sem_to_a (obj)
     VALUE obj;
{
  struct ipcid_ds *semid;
  int i, nsems;
  VALUE dst;
  union semun arg;

  semid = get_ipcid_and_stat (obj);
  nsems = semid->semstat.sem_nsems;
  arg.array = (unsigned short int *) ALLOCA_N (unsigned short int, nsems);

  semctl (semid->id, 0, GETALL, arg);

  dst = rb_ary_new ();
  for (i = 0; i < nsems; i++)
    rb_ary_push (dst, INT2FIX (arg.array[i]));

  return dst;
}

/*
 * call-seq:
 *   set_all(array) -> Semaphore
 *
 * Set all values of a semaphore to corresponding values from
 * +array+. Return self. See semctl(2).
 */

static VALUE
rb_sem_set_all (obj, ary)
     VALUE obj, ary;
{
  struct ipcid_ds *semid;
  union semun arg;
  int i, nsems;

  semid = get_ipcid_and_stat (obj);
  nsems = semid->semstat.sem_nsems;

  if (RARRAY(ary)->len != nsems)
    rb_raise (cError, "doesn't match with semnum");

  arg.array = (unsigned short int *) ALLOCA_N (unsigned short int, nsems);
  for (i = 0; i < nsems; i++)
    arg.array[i] = NUM2INT (RARRAY(ary)->ptr[i]);
  semctl (semid->id, 0, SETALL, arg);

  return obj;
}

/*
 * call-seq: value(semnum) -> Fixnum
 *
 * Return the value of semaphore +semnum+. See semctl(2).
 */

static VALUE
rb_sem_value (obj, v_pos)
     VALUE obj, v_pos;
{
  struct ipcid_ds *semid;
  int pos;
  int value;

  semid = get_ipcid_and_stat (obj);
  pos = NUM2INT (v_pos);
  Check_Valid_Semnum (pos, semid);
  value = semctl (semid->id, pos, GETVAL, 0);
  if (value == -1)
    rb_sys_fail ("semctl(2)");
  return INT2FIX (value);
}

/*
 * call-seq: set_value(semnum, value) -> Semaphore
 *
 * Set the value of semaphore +semnum+ to +value+. Return self. See
 * semctl(2).
 */

static VALUE
rb_sem_set_value (obj, v_pos, v_value)
     VALUE obj, v_pos, v_value;
{
  struct ipcid_ds *semid;
  int pos;
  union semun arg;

  semid = get_ipcid_and_stat (obj);
  pos = NUM2INT (v_pos);
  Check_Valid_Semnum (pos, semid);
  arg.val = NUM2INT(v_value);
  if (semctl (semid->id, pos, SETVAL, arg) == -1)
    rb_sys_fail ("semctl(2)");
  return obj;
}

/*
 * call-seq:
 *   n_count(semnum) -> Fixnum
 *
 * Return the number of processes waiting for the value semaphore
 * +semnum+ to increase. See semctl(2).
 *
 * *Note*: Ruby threads waiting for a semaphore do not increment
 * this counter. In a multi-threaded program, the SystemVIPC
 * module emulates waiting by repeatedly calling the underlying
 * semop(2) with the IPC_NOWAIT flag set and sleeping between calls.
 */

static VALUE
rb_sem_ncnt (obj, v_pos)
     VALUE obj, v_pos;
{
  struct ipcid_ds *semid;
  int ncnt, pos;

  semid = get_ipcid_and_stat (obj);
  pos = NUM2INT (v_pos);
  Check_Valid_Semnum (pos, semid);
  ncnt = semctl (semid->id, pos, GETNCNT, 0);
  if (ncnt == -1)
    rb_sys_fail ("semctl(2)");
  return INT2FIX (ncnt);
}

/*
 * call-seq:
 *   z_count(semnum) -> Fixnum
 *
 * Return the number of processes waiting for the value semaphore
 * +semnum+ to become zero. See semctl(2).
 *
 * *Note*: Ruby threads waiting for a semaphore do not increment
 * this counter. In a multi-threaded program, the SystemVIPC
 * module emulates waiting by repeatedly calling the underlying
 * semop(2) with the IPC_NOWAIT flag set and sleeping between calls.
 */

static VALUE
rb_sem_zcnt (obj, v_pos)
     VALUE obj, v_pos;
{
  struct ipcid_ds *semid;
  int zcnt, pos;

  semid = get_ipcid_and_stat (obj);
  pos = NUM2INT (v_pos);
  Check_Valid_Semnum (pos, semid);
  zcnt = semctl (semid->id, pos, GETZCNT, 0);
  if (zcnt == -1)
    rb_sys_fail ("semctl(2)");
  return INT2FIX (zcnt);
}

/*
 * call-seq:
 *   pid(semnum) -> Fixnum
 *
 * Return the PID of the process that executed the last semop()
 * call for semaphore +semnum+. See semctl(2).
 */

static VALUE
rb_sem_pid (obj, v_pos)
     VALUE obj, v_pos;
{
  struct ipcid_ds *semid;
  int pid, pos;

  semid = get_ipcid_and_stat (obj);
  pos = NUM2INT (v_pos);
  Check_Valid_Semnum (pos, semid);
  pid = semctl (semid->id, pos, GETPID, 0);
  if (pid == -1)
    rb_sys_fail ("semctl(2)");
  return INT2FIX (pid);
}

/*
 * call-seq:
 *   size -> Fixnum
 *
 * Return the number of semaphores in the set.  See semctl(2).
 */

static VALUE
rb_sem_size (obj)
     VALUE obj;
{
  struct ipcid_ds *semid;
  semid = get_ipcid_and_stat (obj);
  return INT2FIX (semid->semstat.sem_nsems);
}

/*
 * call-seq:
 *   apply(array) -> Semaphore
 *
 * Apply an +array+ of SemaphoreOperation elements.  See semop(2).
 */

static VALUE
rb_sem_apply (obj, ary)
     VALUE obj, ary;
{
  struct ipcid_ds *semid;
  struct sembuf *array;
  int nsops, i, nsems, error, nowait = 0;

  semid = get_ipcid_and_stat (obj);
  nsems = semid->semstat.sem_nsems;
  nsops = RARRAY(ary)->len;
  array = (struct sembuf *) ALLOCA_N (struct sembuf, nsems);
  for (i = 0; i < nsops; i++)
    {
      struct sembuf *op;
      Data_Get_Struct (RARRAY(ary)->ptr[i], struct sembuf, op);
      nowait = nowait && (op->sem_flg & IPC_NOWAIT);
      if (!rb_thread_alone()) op->sem_flg |= IPC_NOWAIT;
      memcpy (&array[i], op, sizeof (struct sembuf));
      Check_Valid_Semnum (array[i].sem_num, semid);
    }
      
 retry:
  TRAP_BEG;
  error = semop (semid->id, array, nsops);
  TRAP_END;
  if (error == -1)
    {
      switch (errno)
	{
	case EINTR:
	    goto retry;
	case EWOULDBLOCK:
#if EAGAIN != EWOULDBLOCK
	case EAGAIN:
#endif
	  if (!nowait)
	    {
	      rb_thread_polling ();
	      goto retry;
	    }
	}
      rb_sys_fail ("semop(2)");
    }

  return obj;
}

static void
shm_stat (shmid)
     struct ipcid_ds *shmid;
{
  if (shmctl (shmid->id, IPC_STAT, &shmid->shmstat) == -1)
    rb_sys_fail ("shmctl(2)");
}

static struct ipc_perm *
shm_perm (shmid)
     struct ipcid_ds *shmid;
{
  return &shmid->shmstat.shm_perm;
}

static void
shm_rmid (shmid)
     struct ipcid_ds *shmid;
{
  if (shmid->id < 0)
    rb_raise (cError, "already removed");
  if (shmctl (shmid->id, IPC_RMID, 0) == -1)
    rb_sys_fail ("shmctl(2)");
  shmid->id = -1;
}

/*
 * call-seq:
 *   SharedMemory.new(key, size = 0, shmflg = 0) -> SharedMemory
 *
 * Return a SharedMemory object encapsulating the
 * shared memory segment associated with +key+. See shmget(2).
 */

static VALUE
rb_shm_s_new (argc, argv, klass)
     int argc;
     VALUE *argv, klass;
{
  struct ipcid_ds *shmid;
  VALUE dst, v_key, v_size, v_shmflg;
  int size = 0;

  dst = Data_Make_Struct (klass, struct ipcid_ds, NULL, free, shmid);
  rb_scan_args (argc, argv, "12", &v_key, &v_size, &v_shmflg);
  if (!NIL_P (v_size))
    size = NUM2INT (v_size);
  if (!NIL_P (v_shmflg))
    shmid->flags = NUM2INT (v_shmflg);
  shmid->id = shmget ((key_t)NUM2INT (v_key), size, shmid->flags);
  if (shmid->id == -1)
    rb_sys_fail ("shmget(2)");
  shmid->stat = shm_stat;
  shmid->perm = shm_perm;
  shmid->rmid = shm_rmid;

  return dst;
}

/*
 * call-seq:
 *   attach(shmflg = 0) -> SharedMemory
 *
 * Attach the shared memory segment. See shmat(2).
 */

static VALUE
rb_shm_attach (argc, argv, obj)
     int argc;
     VALUE *argv, obj;
{
  VALUE v_flags;
  struct ipcid_ds *shmid;
  int flags = 0;
  void *data;

  shmid = get_ipcid (obj);
  if (shmid->data)
    rb_raise (cError, "already attached");

  rb_scan_args (argc, argv, "01", &v_flags);
  if (!NIL_P (v_flags))
    flags = NUM2INT (v_flags);

  data = shmat (shmid->id, 0, flags);
  if (data == (void*)-1)
    rb_sys_fail ("shmat(2)");
  shmid->data = data;

  return obj;
}

/*
 * call-seq:
 *   detach -> SharedMemory
 *
 * Detach the shared memory segment. See shmdt(2).
 */

static VALUE
rb_shm_detach (obj)
     VALUE obj;
{
  struct ipcid_ds *shmid;

  shmid = get_ipcid (obj);
  if (!shmid->data)
    rb_raise (cError, "already detached");

  if (shmdt (shmid->data) == -1)
    rb_sys_fail ("shmdt(2)");
  shmid->data = NULL;

  return obj;
}

#define Check_Valid_Shm_Segsz(n, shmid)		\
  if (n > shmid->shmstat.shm_segsz)		\
    rb_raise (cError, "invalid shm_segsz")

/*
 * call-seq:
 *   read(len = 0, offset = 0) -> String
 *
 * Read +len+ bytes from the shared memory segment starting at
 * optional +offset+.
 */

static VALUE
rb_shm_read (argc, argv, obj)
     int argc;
     VALUE *argv, obj;
{
  struct ipcid_ds *shmid;
  VALUE v_len, v_offset;
  int len, offset = 0;

  shmid = get_ipcid (obj);
  if (!shmid->data)
    rb_raise (cError, "detached memory");
  shmid->stat (shmid);

  len = shmid->shmstat.shm_segsz;

  rb_scan_args (argc, argv, "11", &v_len, &v_offset);
  if (!NIL_P (v_len))
    len = NUM2INT (v_len);
  if (!NIL_P (v_offset))
    offset = NUM2INT (v_offset);
  Check_Valid_Shm_Segsz (len + offset, shmid);

  return rb_str_new (shmid->data + offset, len);
}

/*
 * call-seq:
 *   write(buf, offset = 0) -> SharedMemory
 *
 * Write data from +buf+ to the shared memory segment at
 * optional +offset+ offset.
 */

static VALUE
rb_shm_write (argc, argv, obj)
     int argc;
     VALUE *argv, obj;
{
  struct ipcid_ds *shmid;
  int i, len, offset = 0;
  char *buf;
  VALUE v_buf;

  shmid = get_ipcid (obj);
  if (!shmid->data)
    rb_raise (cError, "detached memory");
  shmid->stat (shmid);

  v_buf = argv[0];

  if (argc == 2)
    offset = NUM2INT (argv[1]);

  len = RSTRING (v_buf)->len;
  Check_Valid_Shm_Segsz (len + offset, shmid);

  buf = shmid->data + offset;

  for (i = 0; i < len; i++)
    *buf++ = RSTRING (v_buf)->ptr[i];

  return obj;
}

/*
 * call-seq:
 *   size -> Fixnum
 *
 * Return the size of the shared memory segment.
 */

static VALUE
rb_shm_size (obj)
     VALUE obj;
{
  struct ipcid_ds *shmid;
  shmid = get_ipcid_and_stat (obj);
  return INT2FIX (shmid->shmstat.shm_segsz);
}

/*
 * call-seq:
 *   SemaphoreOperation.new(pos, value, flags = 0) -> SemaphoreOperation
 *
 * Create a new SemaphoreOperation. +pos+ is an index identifying
 * a particular semaphore within a semaphore set. +value+ is the
 * value to added or subtracted from the semaphore. +flags+ is a
 * bitwise OR selected from IPC_NOWAIT and SEM_UNDO. See semop(2).
 */

static VALUE
rb_semop_s_new (argc, argv, klass)
     int argc;
     VALUE *argv, klass;
{
  struct sembuf *op;
  VALUE dst, v_pos, v_value, v_flags;

  dst = Data_Make_Struct (klass, struct sembuf, NULL, free, op);
  rb_scan_args (argc, argv, "21", &v_pos, &v_value, &v_flags);
  op->sem_num = NUM2INT (v_pos);
  op->sem_op = NUM2INT (v_value);
  if (!NIL_P (v_flags))
    op->sem_flg = NUM2INT (v_flags);

  return dst;
}

/*
 * call-seq:
 *   pos -> Fixnum
 *
 * Return the operation semaphore position. See semop(2).
 */

static VALUE
rb_semop_pos (obj)
     VALUE obj;
{
  struct sembuf *op;

  Data_Get_Struct (obj, struct sembuf, op);
  return INT2FIX (op->sem_num);
}

/*
 * call-seq:
 *   value -> Fixnum
 *
 * Return the operation value. See semop(2).
 */

static VALUE
rb_semop_value (obj)
     VALUE obj;
{
  struct sembuf *op;

  Data_Get_Struct (obj, struct sembuf, op);
  return INT2FIX (op->sem_op);
}

/*
 * call-seq:
 *   flags -> Fixnum
 *
 * Return the operation flags. See semop(2).
 */

static VALUE
rb_semop_flags (obj)
     VALUE obj;
{
  struct sembuf *op;

  Data_Get_Struct (obj, struct sembuf, op);
  return INT2FIX (op->sem_flg);
}

/* call-seq:
 *   Permission.new(ipcobject) -> Permission
 *
 * Create a Permission object for +ipcobject+.
 */

static VALUE
rb_perm_s_new (klass, v_ipcid)
     VALUE klass, v_ipcid;
{
  struct ipcid_ds *ipcid;

  Data_Get_Struct (v_ipcid, struct ipcid_ds, ipcid);
  ipcid->stat (ipcid);

  return Data_Wrap_Struct (klass, NULL, NULL, ipcid->perm (ipcid));
}

/*
 * call-seq:
 *   cuid -> Fixnum
 *
 * Return effective UID of creator.
 */

static VALUE
rb_perm_cuid (obj)
     VALUE obj;
{
  struct ipc_perm *perm;

  Data_Get_Struct (obj, struct ipc_perm, perm);
  return INT2FIX (perm->cuid);
}

/*
 * call-seq:
 *   cgid -> Fixnum
 *
 * Return effective GID of creator.
 */

static VALUE
rb_perm_cgid (obj)
     VALUE obj;
{
  struct ipc_perm *perm;

  Data_Get_Struct (obj, struct ipc_perm, perm);
  return INT2FIX (perm->cgid);
}

/*
 * call-seq:
 *   uid -> Fixnum
 *
 * Return effective UID of owner.
 */

static VALUE
rb_perm_uid (obj)
     VALUE obj;
{
  struct ipc_perm *perm;

  Data_Get_Struct (obj, struct ipc_perm, perm);
  return INT2FIX (perm->uid);
}

/*
 * call-seq:
 *   gid -> Fixnum
 *
 * Return effective GID of owner.
 */

static VALUE
rb_perm_gid (obj)
     VALUE obj;
{
  struct ipc_perm *perm;

  Data_Get_Struct (obj, struct ipc_perm, perm);
  return INT2FIX (perm->gid);
}

/*
 * call-seq:
 *   mode -> Fixnum
 *
 * Return mode bits.
 */

static VALUE
rb_perm_mode (obj)
     VALUE obj;
{
  struct ipc_perm *perm;

  Data_Get_Struct (obj, struct ipc_perm, perm);
  return INT2FIX (perm->mode);
}

/*
 * Document-class: SystemVIPC
 *
 * = SystemVIPC
 *
 * Ruby module for System V Inter-Process Communication:
 * message queues, semaphores, and shared memory.
 *
 * Hosted as project sysvipc[http://rubyforge.org/projects/sysvipc/]
 * on RubyForge[http://rubyforge.org/].
 *
 * Copyright (C) 2001, 2006, 2007  Daiki Ueno
 * Copyright (C) 2006, 2007  James Steven Jenkins
 *
 * == Usage Synopsis
 * === Common Code
 *
 * All programs using this module must include
 *
 *     require 'sysvipc'
 *
 * It may be convenient to add
 *
 *     include SystemVIPC
 *
 * All IPC objects are identified by a key. SystemVIPC includes a
 * convenience function for mapping file names and integer IDs into a
 * key:
 *
 *     key = ftok('/a/file/that/must/exist', 0)
 *
 * Any IPC object +ipc+ can be removed after use by
 *
 *     ipc.remove
 *
 * === Message Queues
 *
 * Get (create if necessary) a message queue:
 *
 *     mq = MessageQueue.new(key, IPC_CREAT | 0600)
 *
 * Send a message of type 0:
 *
 *     mq.send(0, 'message')
 *
 * Receive up to 100 bytes from the first message of type 0:
 *
 *     msg = mq.recv(0, 100)
 *
 * === Semaphores
 *
 * Get (create if necessary) a set of 5 semaphores:
 *
 *     sm = Semaphore.new(key, 5, IPC_CREAT | 0600)
 *
 * Initialize semaphores if newly created:
 *
 *     sm.set_all(Array.new(5, 1)) if sm.pid(0) == 0
 *
 * Acquire semaphore 2 (waiting if necessary):
 *
 *     sm.apply([SemaphoreOperation.new(2, -1)])
 *
 * Release semaphore 2:
 *
 *     sm.apply([SemaphoreOperation.new(2, 1)])
 *
 * === Shared Memory
 *
 * Get (create if necessary) an 8192-byte shared memory region:
 *
 *     sh = SharedMemory(key, 8192, IPC_CREAT | 0660)
 *
 * Attach shared memory:
 *
 *     sh.attach
 *
 * Write data:
 *
 *     sh.write('testing')
 *
 * Read 100 bytes of data:
 *
 *     data = sh.read(100);
 *
 * Detach shared memory:
 *
 *     sh.detach
 *
 * == Installation
 *
 * 1. <tt>ruby extconf.rb</tt>
 * 2. <tt>make</tt>
 * 3. <tt>make install</tt> (requires appropriate privilege)
 *
 * == Testing
 *
 * 1. <tt>./test_sysvipc</tt>
 */

void Init_sysvipc ()
{
  VALUE mSystemVIPC, cPermission, cIPCObject, cSemaphoreOparation;
  VALUE cMessageQueue, cSemaphore, cSharedMemory;

  mSystemVIPC = rb_define_module ("SystemVIPC");
  rb_define_module_function (mSystemVIPC, "ftok", rb_ftok, 2);

  cPermission =
    rb_define_class_under (mSystemVIPC, "Permission", rb_cObject);
  rb_define_singleton_method (cPermission, "new", rb_perm_s_new, 1);
  rb_define_method (cPermission, "cuid", rb_perm_cuid, 0);
  rb_define_method (cPermission, "cgid", rb_perm_cgid, 0);
  rb_define_method (cPermission, "uid", rb_perm_uid, 0);
  rb_define_method (cPermission, "gid", rb_perm_gid, 0);
  rb_define_method (cPermission, "mode", rb_perm_mode, 0);

  cIPCObject =
    rb_define_class_under (mSystemVIPC, "IPCObject", rb_cObject);
  rb_define_method (cIPCObject, "remove", rb_ipc_remove, 0);
  rb_undef_method (CLASS_OF (cIPCObject), "new");

  cSemaphoreOparation =
    rb_define_class_under (mSystemVIPC, "SemaphoreOperation", rb_cObject);
  rb_define_singleton_method (cSemaphoreOparation, "new", rb_semop_s_new, -1);
  rb_define_method (cSemaphoreOparation, "pos", rb_semop_pos, 0);
  rb_define_method (cSemaphoreOparation, "value", rb_semop_value, 0);
  rb_define_method (cSemaphoreOparation, "flags", rb_semop_flags, 0);

  cError =
    rb_define_class_under (mSystemVIPC, "Error", rb_eStandardError);
  cMessageQueue =
    rb_define_class_under (mSystemVIPC, "MessageQueue", cIPCObject);
  rb_define_singleton_method (cMessageQueue, "new", rb_msg_s_new, -1);
  rb_define_method (cMessageQueue, "send", rb_msg_send, -1);
  rb_define_method (cMessageQueue, "recv", rb_msg_recv, -1);

  cSemaphore =
    rb_define_class_under (mSystemVIPC, "Semaphore", cIPCObject);
  rb_define_singleton_method (cSemaphore, "new", rb_sem_s_new, -1);
  rb_define_method (cSemaphore, "to_a", rb_sem_to_a, 0);
  rb_define_method (cSemaphore, "set_all", rb_sem_set_all, 1);
  rb_define_method (cSemaphore, "value", rb_sem_value, 1);
  rb_define_method (cSemaphore, "set_value", rb_sem_set_value, 2);
  rb_define_method (cSemaphore, "n_count", rb_sem_ncnt, 1);
  rb_define_method (cSemaphore, "z_count", rb_sem_zcnt, 1);
  rb_define_method (cSemaphore, "pid", rb_sem_pid, 1);
  rb_define_method (cSemaphore, "apply", rb_sem_apply, 1);
  rb_define_method (cSemaphore, "size", rb_sem_size, 0);

  cSharedMemory =
    rb_define_class_under (mSystemVIPC, "SharedMemory", cIPCObject);
  rb_define_singleton_method (cSharedMemory, "new", rb_shm_s_new, -1);
  rb_define_method (cSharedMemory, "attach", rb_shm_attach, -1);
  rb_define_method (cSharedMemory, "detach", rb_shm_detach, 0);
  rb_define_method (cSharedMemory, "read", rb_shm_read, -1);
  rb_define_method (cSharedMemory, "write", rb_shm_write, -1);
  rb_define_method (cSharedMemory, "size", rb_shm_size, 0);

  rb_define_const (mSystemVIPC, "IPC_PRIVATE", INT2FIX (IPC_PRIVATE));
  rb_define_const (mSystemVIPC, "IPC_CREAT", INT2FIX (IPC_CREAT));
  rb_define_const (mSystemVIPC, "IPC_EXCL", INT2FIX (IPC_EXCL));
  rb_define_const (mSystemVIPC, "IPC_NOWAIT", INT2FIX (IPC_NOWAIT));
  rb_define_const (mSystemVIPC, "SEM_UNDO", INT2FIX (SEM_UNDO));
}


syntax highlighted by Code2HTML, v. 0.9.1