+ *retval = msgsz;
+ eval = 0;
+msgrcvout:
+ SYSV_MSG_SUBSYS_UNLOCK();
+ return eval;
+}
+
+static int
+IPCS_msg_sysctl(__unused struct sysctl_oid *oidp, __unused void *arg1,
+ __unused int arg2, struct sysctl_req *req)
+{
+ int error;
+ int cursor;
+ union {
+ struct user32_IPCS_command u32;
+ struct user_IPCS_command u64;
+ } ipcs = { };
+ struct user32_msqid_ds msqid_ds32 = {}; /* post conversion, 32 bit version */
+ struct user64_msqid_ds msqid_ds64 = {}; /* post conversion, 64 bit version */
+ void *msqid_dsp;
+ size_t ipcs_sz;
+ size_t msqid_ds_sz;
+ struct proc *p = current_proc();
+
+ if (IS_64BIT_PROCESS(p)) {
+ ipcs_sz = sizeof(struct user_IPCS_command);
+ msqid_ds_sz = sizeof(struct user64_msqid_ds);
+ } else {
+ ipcs_sz = sizeof(struct user32_IPCS_command);
+ msqid_ds_sz = sizeof(struct user32_msqid_ds);
+ }
+
+ /* Copy in the command structure */
+ if ((error = SYSCTL_IN(req, &ipcs, ipcs_sz)) != 0) {
+ return error;
+ }
+
+ if (!IS_64BIT_PROCESS(p)) { /* convert in place */
+ ipcs.u64.ipcs_data = CAST_USER_ADDR_T(ipcs.u32.ipcs_data);
+ }
+
+ /* Let us version this interface... */
+ if (ipcs.u64.ipcs_magic != IPCS_MAGIC) {
+ return EINVAL;
+ }
+
+ SYSV_MSG_SUBSYS_LOCK();
+
+ switch (ipcs.u64.ipcs_op) {
+ case IPCS_MSG_CONF: /* Obtain global configuration data */
+ if (ipcs.u64.ipcs_datalen != sizeof(struct msginfo)) {
+ error = ERANGE;
+ break;
+ }
+ if (ipcs.u64.ipcs_cursor != 0) { /* fwd. compat. */
+ error = EINVAL;
+ break;
+ }
+ SYSV_MSG_SUBSYS_UNLOCK();
+ error = copyout(&msginfo, ipcs.u64.ipcs_data, ipcs.u64.ipcs_datalen);
+ SYSV_MSG_SUBSYS_LOCK();
+ break;
+
+ case IPCS_MSG_ITER: /* Iterate over existing segments */
+ /* Not done up top so we can set limits via sysctl (later) */
+ if (!msginit(0)) {
+ error = ENOMEM;
+ break;
+ }
+
+ cursor = ipcs.u64.ipcs_cursor;
+ if (cursor < 0 || cursor >= msginfo.msgmni) {
+ error = ERANGE;
+ break;
+ }
+ if (ipcs.u64.ipcs_datalen != (int)msqid_ds_sz) {
+ error = EINVAL;
+ break;
+ }
+ for (; cursor < msginfo.msgmni; cursor++) {
+ if (msqids[cursor].u.msg_qbytes != 0) { /* allocated */
+ break;
+ }
+ continue;
+ }
+ if (cursor == msginfo.msgmni) {
+ error = ENOENT;
+ break;
+ }
+
+ msqid_dsp = &msqids[cursor]; /* default: 64 bit */
+
+ /*
+ * If necessary, convert the 64 bit kernel segment
+ * descriptor to a 32 bit user one.
+ */
+ if (IS_64BIT_PROCESS(p)) {
+ msqid_ds_kerneltouser64(msqid_dsp, &msqid_ds64);
+ msqid_dsp = &msqid_ds64;
+ } else {
+ msqid_ds_kerneltouser32(msqid_dsp, &msqid_ds32);
+ msqid_dsp = &msqid_ds32;
+ }
+
+ SYSV_MSG_SUBSYS_UNLOCK();
+ error = copyout(msqid_dsp, ipcs.u64.ipcs_data, ipcs.u64.ipcs_datalen);
+ if (!error) {
+ /* update cursor */
+ ipcs.u64.ipcs_cursor = cursor + 1;
+
+ if (!IS_64BIT_PROCESS(p)) { /* convert in place */
+ ipcs.u32.ipcs_data = CAST_DOWN_EXPLICIT(user32_addr_t, ipcs.u64.ipcs_data);
+ }
+ error = SYSCTL_OUT(req, &ipcs, ipcs_sz);
+ }
+ SYSV_MSG_SUBSYS_LOCK();
+ break;
+
+ default:
+ error = EINVAL;
+ break;
+ }
+
+ SYSV_MSG_SUBSYS_UNLOCK();
+ return error;