github.com/teepark/go-sysvipc@v0.0.0-20200817232735-d7ca6053ea29/msg.go (about)

     1  package sysvipc
     2  
     3  /*
     4  #include <sys/types.h>
     5  #include <sys/ipc.h>
     6  #include <sys/msg.h>
     7  int msgget(key_t key, int msgflg);
     8  int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
     9  ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
    10  int msgctl(int msqid, int cmd, struct msqid_ds *buf);
    11  */
    12  import "C"
    13  import (
    14  	"time"
    15  	"unsafe"
    16  )
    17  
    18  // MessageQueue is a kernel-maintained queue.
    19  type MessageQueue int64
    20  
    21  // GetMsgQueue creates or retrieves a message queue id for a given IPC key.
    22  func GetMsgQueue(key int64, flags *MQFlags) (MessageQueue, error) {
    23  	rc, err := C.msgget(C.key_t(key), C.int(flags.flags()))
    24  	if rc == -1 {
    25  		return -1, err
    26  	}
    27  	return MessageQueue(rc), nil
    28  }
    29  
    30  // Send places a new message onto the queue
    31  func (mq MessageQueue) Send(mtyp int64, body []byte, flags *MQSendFlags) error {
    32  	b := make([]byte, len(body)+8)
    33  	copy(b[:8], serialize(mtyp))
    34  	copy(b[8:], body)
    35  
    36  	rc, err := C.msgsnd(
    37  		C.int(mq),
    38  		unsafe.Pointer(&b[0]),
    39  		C.size_t(len(body)),
    40  		C.int(flags.flags()),
    41  	)
    42  	if rc == -1 {
    43  		return err
    44  	}
    45  	return nil
    46  }
    47  
    48  // Receive retrieves a message from the queue.
    49  func (mq MessageQueue) Receive(maxlen uint, msgtyp int64, flags *MQRecvFlags) ([]byte, int64, error) {
    50  	b := make([]byte, maxlen+8)
    51  
    52  	rc, err := C.msgrcv(
    53  		C.int(mq),
    54  		unsafe.Pointer(&b[0]),
    55  		C.size_t(maxlen),
    56  		C.long(msgtyp),
    57  		C.int(flags.flags()),
    58  	)
    59  	if rc == -1 {
    60  		return nil, 0, err
    61  	}
    62  
    63  	mtyp := deserialize(b[:8])
    64  	return b[8 : rc+8], mtyp, nil
    65  }
    66  
    67  // Stat produces information about the queue.
    68  func (mq MessageQueue) Stat() (*MQInfo, error) {
    69  	mqds := C.struct_msqid_ds{}
    70  
    71  	rc, err := C.msgctl(C.int(mq), C.IPC_STAT, &mqds)
    72  	if rc == -1 {
    73  		return nil, err
    74  	}
    75  
    76  	mqinf := MQInfo{
    77  		Perms: IpcPerms{
    78  			OwnerUID:   int(mqds.msg_perm.uid),
    79  			OwnerGID:   int(mqds.msg_perm.gid),
    80  			CreatorUID: int(mqds.msg_perm.cuid),
    81  			CreatorGID: int(mqds.msg_perm.cgid),
    82  			Mode:       uint16(mqds.msg_perm.mode),
    83  		},
    84  		LastSend:   time.Unix(int64(mqds.msg_stime), 0),
    85  		LastRcv:    time.Unix(int64(mqds.msg_rtime), 0),
    86  		LastChange: time.Unix(int64(mqds.msg_ctime), 0),
    87  		MsgCount:   uint(mqds.msg_qnum),
    88  		MaxBytes:   uint(mqds.msg_qbytes),
    89  		LastSender: int(mqds.msg_lspid),
    90  		LastRcver:  int(mqds.msg_lrpid),
    91  	}
    92  	return &mqinf, nil
    93  }
    94  
    95  // Set updates parameters of the queue.
    96  func (mq MessageQueue) Set(mqi *MQInfo) error {
    97  	mqds := &C.struct_msqid_ds{
    98  		msg_perm: C.struct_ipc_perm{
    99  			uid:  C.__uid_t(mqi.Perms.OwnerUID),
   100  			gid:  C.__gid_t(mqi.Perms.OwnerGID),
   101  			mode: C.ushort(mqi.Perms.Mode & 0x1FF),
   102  		},
   103  		msg_qbytes: C.msglen_t(mqi.MaxBytes),
   104  	}
   105  
   106  	rc, err := C.msgctl(C.int(mq), C.IPC_SET, mqds)
   107  	if rc == -1 {
   108  		return err
   109  	}
   110  	return nil
   111  }
   112  
   113  // Remove deletes the queue.
   114  // This will also awake all waiting readers and writers with EIDRM.
   115  func (mq MessageQueue) Remove() error {
   116  	rc, err := C.msgctl(C.int(mq), C.IPC_RMID, nil)
   117  	if rc == -1 {
   118  		return err
   119  	}
   120  	return nil
   121  }
   122  
   123  // MQInfo holds meta information about a message queue.
   124  type MQInfo struct {
   125  	Perms      IpcPerms
   126  	LastSend   time.Time
   127  	LastRcv    time.Time
   128  	LastChange time.Time
   129  
   130  	MsgCount uint
   131  	MaxBytes uint
   132  
   133  	LastSender int
   134  	LastRcver  int
   135  }
   136  
   137  // MQFlags holds the flags/options for GetMsgQueue
   138  type MQFlags struct {
   139  	// Create controls whether to create the queue if it doesn't exist.
   140  	Create bool
   141  
   142  	// Exclusive causes GetMsgQueue to fail if the queue already exists (only
   143  	// useful with Create).
   144  	Exclusive bool
   145  
   146  	// Perms is the file-style (rwxrwxrwx) permissions with which to create the
   147  	// queue (also only useful with Create).
   148  	Perms int
   149  }
   150  
   151  func (mf *MQFlags) flags() int64 {
   152  	if mf == nil {
   153  		return 0
   154  	}
   155  
   156  	var f int64 = int64(mf.Perms) & 0777
   157  	if mf.Create {
   158  		f |= int64(C.IPC_CREAT)
   159  	}
   160  	if mf.Exclusive {
   161  		f |= int64(C.IPC_EXCL)
   162  	}
   163  
   164  	return f
   165  }
   166  
   167  // MQSendFlags hold the options for a MessageQueue.Send()
   168  type MQSendFlags struct {
   169  	// DontWait causes Send() calls that would otherwise
   170  	// block to instead fail with syscall.EAGAIN
   171  	DontWait bool
   172  }
   173  
   174  func (mf *MQSendFlags) flags() int64 {
   175  	if mf == nil {
   176  		return 0
   177  	}
   178  
   179  	var f int64
   180  	if mf.DontWait {
   181  		f |= int64(C.IPC_NOWAIT)
   182  	}
   183  
   184  	return f
   185  }
   186  
   187  // MQRecvFlags hold the options for a MessageQueue.Receive()
   188  type MQRecvFlags struct {
   189  	// DontWait causes Receive() calls that would otherwise
   190  	// block to instead fail with syscall.EAGAIN or syscall.ENOMSG
   191  	DontWait bool
   192  
   193  	// Truncate allows shortening the message if maxlen is
   194  	// shorter than the message being received
   195  	Truncate bool
   196  }
   197  
   198  func (mf *MQRecvFlags) flags() int64 {
   199  	if mf == nil {
   200  		return 0
   201  	}
   202  
   203  	var f int64
   204  	if mf.DontWait {
   205  		f |= int64(C.IPC_NOWAIT)
   206  	}
   207  	if mf.Truncate {
   208  		f |= int64(C.MSG_NOERROR)
   209  	}
   210  
   211  	return f
   212  }
   213  
   214  /*
   215  real c-style pointer casting
   216  */
   217  
   218  func serialize(num int64) []byte {
   219  	b := make([]byte, 8)
   220  	base := uintptr(unsafe.Pointer(&num))
   221  	for i := 0; i < 8; i++ {
   222  		b[i] = *(*byte)(unsafe.Pointer(base + uintptr(i)))
   223  	}
   224  	return b
   225  }
   226  
   227  func deserialize(b []byte) int64 {
   228  	return *(*int64)(unsafe.Pointer(&b[0]))
   229  }