-
Notifications
You must be signed in to change notification settings - Fork 17
/
wrapper.go
162 lines (129 loc) · 3.93 KB
/
wrapper.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
package sysv_mq
/*
#cgo CFLAGS: -O2
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <sys/msg.h>
typedef struct _sysv_msg {
long mtype;
char mtext[1];
} sysv_msg;
*/
import "C"
import "unsafe"
import "errors"
const (
IPC_CREAT = C.IPC_CREAT
IPC_EXCL = C.IPC_EXCL
IPC_NOWAIT = C.IPC_NOWAIT
IPC_STAT = C.IPC_STAT
IPC_SET = C.IPC_SET
IPC_RMID = C.IPC_RMID
MemoryAllocationError = "malloc failed to allocate memory"
MessageBiggerThanBuffer = "message length is longer than the size of the buffer"
)
// msgop(2)
// int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg)
func msgsnd(key int, message []byte, buffer *C.sysv_msg, maxSize int, mtype int, flags int) error {
if len(message) > maxSize {
return errors.New(MessageBiggerThanBuffer)
}
msgSize := C.size_t(len(message))
buffer.mtype = C.long(mtype)
if msgSize > 0 {
C.memcpy(unsafe.Pointer(&buffer.mtext), unsafe.Pointer(&message[0]), msgSize)
}
_, err := C.msgsnd(C.int(key), unsafe.Pointer(buffer), msgSize, C.int(flags))
return err
}
// msgop(2)
// ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
func msgrcv(key int, mtype int, buffer *C.sysv_msg, strSize int, flags int) ([]byte, int, error) {
length, err := C.msgrcv(C.int(key), unsafe.Pointer(buffer), C.size_t(strSize), C.long(mtype), C.int(flags))
if err != nil {
return nil, 0, err
}
return C.GoBytes(unsafe.Pointer(&buffer.mtext), C.int(length)), int(buffer.mtype), nil
}
// msgget(2)
// int msgget(key_t key, int msgflg);
func msgget(key int, mode int) (int, error) {
res, err := C.msgget(C.key_t(key), C.int(mode))
if err != nil {
return -1, err
}
return int(res), nil
}
// ftok(3):
// key_t ftok(const char *pathname, int proj_id);
func ftok(path string, projId int) (int, error) {
cs := C.CString(path)
if cs == nil {
return 0, errors.New(MemoryAllocationError)
}
defer C.free(unsafe.Pointer(cs))
res, err := C.ftok(cs, C.int(projId))
if err != nil {
return -1, err
}
return int(res), nil
}
// msgctl(2)
// int msgctl(int msqid, int cmd, struct msqid_ds *buf);
func msgctl(key int, cmd int) (*C.struct_msqid_ds, error) {
info := new(C.struct_msqid_ds)
_, err := C.msgctl(C.int(key), C.int(cmd), info)
return info, err
}
// The buffer is malloced, and not handled by Go, because SysV MQs do
// runtime-length inline arrays that Go does not support without a bunch of reflection
func allocateBuffer(strSize int) (*C.sysv_msg, error) {
// you can't reliably take the size of C structs from go (yay platform-dependent padding/alignment
// differences) so we manually construct what should basically just be sizeof(C.sysv_msg) + strSize.
// Fortunately there's only one other member besides the variable-length buffer, so it's not too bad.
bufferSize := C.size_t(strSize) + C.size_t(unsafe.Sizeof(C.long(1)))
buffer := (*C.sysv_msg)(C.malloc(bufferSize))
if buffer == nil {
return buffer, errors.New(MemoryAllocationError)
}
return buffer, nil
}
func freeBuffer(buffer *C.sysv_msg) {
C.free(unsafe.Pointer(buffer))
}
// Wraps msgctl(key, IPC_RMID).
func ipcDestroy(key int) error {
_, err := msgctl(key, IPC_RMID)
return err
}
// Wraps msgctl(key, IPC_STAT).
func ipcStat(key int) (*QueueStats, error) {
info, err := msgctl(key, IPC_STAT)
if err != nil {
return nil, err
}
perm := QueuePermissions{
Uid: uint32(info.msg_perm.uid),
Gid: uint32(info.msg_perm.gid),
Cuid: uint32(info.msg_perm.cuid),
Cgid: uint32(info.msg_perm.cgid),
Mode: uint16(info.msg_perm.mode),
}
stat := &QueueStats{
Perm: perm,
Stime: int64(info.msg_stime),
// Rtime: int64(info.msg_rtime), // https://github.com/Shopify/sysv_mq/issues/10
Ctime: int64(info.msg_ctime),
Cbytes: cbytesFromStruct(info),
Qnum: uint64(info.msg_qnum),
Qbytes: uint64(info.msg_qbytes),
Lspid: int32(info.msg_lspid),
Lrpid: int32(info.msg_lrpid),
}
return stat, nil
}