#include "available-stubs.h"

#include <sys/sem.h>
#include <stdarg.h>
#include "checker_api.h"
#include "message.h"
#include <config.h>

#ifdef HAVE_semctl
#ifndef HAVE_SEMUN
union semun {
  int val;
  struct semid_ds *buf;
  ushort *array;
};
#endif

/* From `/usr/include/sys/sem.h:49'.  */
int chkr_stub_semctl (int semid, int semnum, int cmd, ...)
     __asm__ (CHKR_PREFIX ("semctl"));
int
chkr_stub_semctl (int semid, int semnum, int cmd, ...)
{
  int res;
  union semun arg;
  va_list varg;

  va_start (varg, cmd);

  switch (cmd)
    {
    case IPC_STAT:
      arg = va_arg (varg, union semun);
      va_end (varg);
      stubs_chkr_check_addr (arg.buf,
			     sizeof (struct semid_ds), CHKR_TW, "buf");
      res = semctl (semid, semnum, cmd, arg);
      if (res < 0)
	return res;
      stubs_chkr_set_right (arg.buf, sizeof (struct semid_ds), CHKR_RW);
      return res;
      break;
    case IPC_SET:
      arg = va_arg (varg, union semun);
      va_end (varg);
      stubs_chkr_check_addr (arg.buf,
			     sizeof (struct semid_ds), CHKR_TW, "buf");
      res = semctl (semid, semnum, cmd, arg);
      if (res < 0)
	return res;
      stubs_chkr_set_right (&arg.buf->sem_perm.uid,
			    sizeof (struct semid_ds), CHKR_RW);
      stubs_chkr_set_right (&arg.buf->sem_perm.gid,
			    sizeof (struct semid_ds), CHKR_RW);
      stubs_chkr_set_right (&arg.buf->sem_perm.mode,
			    sizeof (struct semid_ds), CHKR_RW);
      return res;
    case SETVAL:
      /* val is an argument.  */
      arg = va_arg (varg, union semun);
      va_end (varg);
      return semctl (semid, semnum, cmd, arg);
      break;
    case IPC_RMID:
      arg.val = 0;
      return semctl (semid, semnum, IPC_RMID, arg);
    default:
      chkr_header (M_IPC_UNIMPLEMENT);
      chkr_printf (M_SEND_DESCRIPTION);
      return 1;		/* means error */
    }
  chkr_abort ();
}
#endif /* HAVE_semctl */

#ifdef HAVE_semget
/* From `/usr/include/sys/sem.h:52'.  */
int chkr_stub_semget (key_t key, int nsems, int semflag)
     __asm__ (CHKR_PREFIX ("semget"));
int
chkr_stub_semget (key_t key, int nsems, int semflag)
{
#if USE_BI_JUMP
  __builtin_jump (semget);
#else
  return semget (key, nsems, semflag);
#endif /* !USE_BI_JUMP */
}
#endif /* HAVE_semget */

#ifdef HAVE_semop
/* From `/usr/include/sys/sem.h:56'.  */
int chkr_stub_semop (int semid, struct sembuf *sops, unsigned int nsops)
     __asm__ (CHKR_PREFIX ("semop"));
int
chkr_stub_semop (int semid, struct sembuf *sops, unsigned int nsops)
{
  stubs_chkr_check_addr (sops,
			 nsops * sizeof (struct sembuf), CHKR_RO, "sops");
#if USE_BI_JUMP
  __builtin_jump (semop);
#else
  return semop (semid, sops, nsops);
#endif
}
#endif /* HAVE_semop */

