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 }