1#include <stdlib.h> 2#include <stdio.h> 3#include <string.h> 4#include <errno.h> 5#include <linux/msg.h> 6#include <fcntl.h> 7 8#define MAX_MSG_SIZE 32 9 10struct msg1 { 11 int msize; 12 long mtype; 13 char mtext[MAX_MSG_SIZE]; 14}; 15 16#define TEST_STRING "Test sysv5 msg" 17#define MSG_TYPE 1 18 19#define ANOTHER_TEST_STRING "Yet another test sysv5 msg" 20#define ANOTHER_MSG_TYPE 26538 21 22struct msgque_data { 23 key_t key; 24 int msq_id; 25 int qbytes; 26 int qnum; 27 int mode; 28 struct msg1 *messages; 29}; 30 31int restore_queue(struct msgque_data *msgque) 32{ 33 int fd, ret, id, i; 34 char buf[32]; 35 36 fd = open("/proc/sys/kernel/msg_next_id", O_WRONLY); 37 if (fd == -1) { 38 printf("Failed to open /proc/sys/kernel/msg_next_id\n"); 39 return -errno; 40 } 41 sprintf(buf, "%d", msgque->msq_id); 42 43 ret = write(fd, buf, strlen(buf)); 44 if (ret != strlen(buf)) { 45 printf("Failed to write to /proc/sys/kernel/msg_next_id\n"); 46 return -errno; 47 } 48 49 id = msgget(msgque->key, msgque->mode | IPC_CREAT | IPC_EXCL); 50 if (id == -1) { 51 printf("Failed to create queue\n"); 52 return -errno; 53 } 54 55 if (id != msgque->msq_id) { 56 printf("Restored queue has wrong id (%d instead of %d)\n", 57 id, msgque->msq_id); 58 ret = -EFAULT; 59 goto destroy; 60 } 61 62 for (i = 0; i < msgque->qnum; i++) { 63 if (msgsnd(msgque->msq_id, &msgque->messages[i].mtype, 64 msgque->messages[i].msize, IPC_NOWAIT) != 0) { 65 printf("msgsnd failed (%m)\n"); 66 ret = -errno; 67 goto destroy; 68 }; 69 } 70 return 0; 71 72destroy: 73 if (msgctl(id, IPC_RMID, 0)) 74 printf("Failed to destroy queue: %d\n", -errno); 75 return ret; 76} 77 78int check_and_destroy_queue(struct msgque_data *msgque) 79{ 80 struct msg1 message; 81 int cnt = 0, ret; 82 83 while (1) { 84 ret = msgrcv(msgque->msq_id, &message.mtype, MAX_MSG_SIZE, 85 0, IPC_NOWAIT); 86 if (ret < 0) { 87 if (errno == ENOMSG) 88 break; 89 printf("Failed to read IPC message: %m\n"); 90 ret = -errno; 91 goto err; 92 } 93 if (ret != msgque->messages[cnt].msize) { 94 printf("Wrong message size: %d (expected %d)\n", ret, 95 msgque->messages[cnt].msize); 96 ret = -EINVAL; 97 goto err; 98 } 99 if (message.mtype != msgque->messages[cnt].mtype) { 100 printf("Wrong message type\n"); 101 ret = -EINVAL; 102 goto err; 103 } 104 if (memcmp(message.mtext, msgque->messages[cnt].mtext, ret)) { 105 printf("Wrong message content\n"); 106 ret = -EINVAL; 107 goto err; 108 } 109 cnt++; 110 } 111 112 if (cnt != msgque->qnum) { 113 printf("Wrong message number\n"); 114 ret = -EINVAL; 115 goto err; 116 } 117 118 ret = 0; 119err: 120 if (msgctl(msgque->msq_id, IPC_RMID, 0)) { 121 printf("Failed to destroy queue: %d\n", -errno); 122 return -errno; 123 } 124 return ret; 125} 126 127int dump_queue(struct msgque_data *msgque) 128{ 129 struct msqid64_ds ds; 130 int kern_id; 131 int i, ret; 132 133 for (kern_id = 0; kern_id < 256; kern_id++) { 134 ret = msgctl(kern_id, MSG_STAT, &ds); 135 if (ret < 0) { 136 if (errno == -EINVAL) 137 continue; 138 printf("Failed to get stats for IPC queue with id %d\n", 139 kern_id); 140 return -errno; 141 } 142 143 if (ret == msgque->msq_id) 144 break; 145 } 146 147 msgque->messages = malloc(sizeof(struct msg1) * ds.msg_qnum); 148 if (msgque->messages == NULL) { 149 printf("Failed to get stats for IPC queue\n"); 150 return -ENOMEM; 151 } 152 153 msgque->qnum = ds.msg_qnum; 154 msgque->mode = ds.msg_perm.mode; 155 msgque->qbytes = ds.msg_qbytes; 156 157 for (i = 0; i < msgque->qnum; i++) { 158 ret = msgrcv(msgque->msq_id, &msgque->messages[i].mtype, 159 MAX_MSG_SIZE, i, IPC_NOWAIT | MSG_COPY); 160 if (ret < 0) { 161 printf("Failed to copy IPC message: %m (%d)\n", errno); 162 return -errno; 163 } 164 msgque->messages[i].msize = ret; 165 } 166 return 0; 167} 168 169int fill_msgque(struct msgque_data *msgque) 170{ 171 struct msg1 msgbuf; 172 173 msgbuf.mtype = MSG_TYPE; 174 memcpy(msgbuf.mtext, TEST_STRING, sizeof(TEST_STRING)); 175 if (msgsnd(msgque->msq_id, &msgbuf.mtype, sizeof(TEST_STRING), 176 IPC_NOWAIT) != 0) { 177 printf("First message send failed (%m)\n"); 178 return -errno; 179 }; 180 181 msgbuf.mtype = ANOTHER_MSG_TYPE; 182 memcpy(msgbuf.mtext, ANOTHER_TEST_STRING, sizeof(ANOTHER_TEST_STRING)); 183 if (msgsnd(msgque->msq_id, &msgbuf.mtype, sizeof(ANOTHER_TEST_STRING), 184 IPC_NOWAIT) != 0) { 185 printf("Second message send failed (%m)\n"); 186 return -errno; 187 }; 188 return 0; 189} 190 191int main(int argc, char **argv) 192{ 193 int msg, pid, err; 194 struct msgque_data msgque; 195 196 msgque.key = ftok(argv[0], 822155650); 197 if (msgque.key == -1) { 198 printf("Can't make key\n"); 199 return -errno; 200 } 201 202 msgque.msq_id = msgget(msgque.key, IPC_CREAT | IPC_EXCL | 0666); 203 if (msgque.msq_id == -1) { 204 printf("Can't create queue\n"); 205 goto err_out; 206 } 207 208 err = fill_msgque(&msgque); 209 if (err) { 210 printf("Failed to fill queue\n"); 211 goto err_destroy; 212 } 213 214 err = dump_queue(&msgque); 215 if (err) { 216 printf("Failed to dump queue\n"); 217 goto err_destroy; 218 } 219 220 err = check_and_destroy_queue(&msgque); 221 if (err) { 222 printf("Failed to check and destroy queue\n"); 223 goto err_out; 224 } 225 226 err = restore_queue(&msgque); 227 if (err) { 228 printf("Failed to restore queue\n"); 229 goto err_destroy; 230 } 231 232 err = check_and_destroy_queue(&msgque); 233 if (err) { 234 printf("Failed to test queue\n"); 235 goto err_out; 236 } 237 return 0; 238 239err_destroy: 240 if (msgctl(msgque.msq_id, IPC_RMID, 0)) { 241 printf("Failed to destroy queue: %d\n", -errno); 242 return -errno; 243 } 244err_out: 245 return err; 246} 247