stdlibc: \!perror()
[meinos.git] / kernel2 / msg.c
blobfc7ff23a58dcc12359eb75ef551ef24e38e94fb0
1 /*
2 meinOS - A unix-like x86 microkernel operating system
3 Copyright (C) 2008 Janosch Gräf <janosch.graef@gmx.net>
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
19 #include <sys/types.h>
20 #include <perm.h>
21 #include <syscall.h>
22 #include <llist.h>
23 #include <string.h>
24 #include <ipc.h>
25 #include <ipc/msg.h>
26 #include <errno.h>
27 #include <debug.h>
29 #define ipc_msg_find(key,id) ipc_find(key,id,IPC_MSG)
31 int ipc_msg_init() {
32 if (syscall_create(SYSCALL_IPC_MSG_GET,ipc_msg_get,1)==-1) return -1;
33 if (syscall_create(SYSCALL_IPC_MSG_CREATE,ipc_msg_create,3)==-1) return -1;
34 if (syscall_create(SYSCALL_IPC_MSG_DESTROY,ipc_msg_destroy,2)==-1) return -1;
35 if (syscall_create(SYSCALL_IPC_MSG_SEND,ipc_msg_send,6)==-1) return -1;
36 if (syscall_create(SYSCALL_IPC_MSG_RECV,ipc_msg_recv,6)==-1) return -1;
37 return 0;
40 /**
41 * Gets MSQID by key (Syscall)
42 * @param key IPC Key
43 * @return MSQID
45 id_t ipc_msg_get(key_t key) {
46 ipc_msg_t *msg = ipc_msg_find(key,-1);
47 if (msg==NULL) return -1;
48 return msg->ipc.id;
51 /**
52 * Creates a Message Queue (Syscall)
53 * @param key IPC key
54 * @param flags Flags
55 * @param time Current time
56 * @return MSQID
58 id_t ipc_msg_create(key_t key,mode_t mode,time_t time) {
59 ipc_msg_t *msg = key!=IPC_PRIVATE?ipc_msg_find(key,-1):NULL;
60 if (msg==NULL) {
61 ipc_msg_t *new = malloc(sizeof(ipc_msg_t));
62 if (new!=NULL) {
63 memset(new,0,sizeof(ipc_msg_t));
64 new->ipc.type = IPC_MSG;
65 new->ipc.key = key;
66 new->ipc.owner = proc_current;
67 new->ipc.creator = proc_current;
68 new->ipc.id = ipc_lastid++;
69 new->ipc.mode = mode;
70 new->msgs = llist_create();
71 new->waiting = llist_create();
72 new->ctime = time;
73 llist_push(ipc_objects,new);
74 return new->ipc.id;
77 return -EINVAL;
80 /**
81 * Destroys a message queue (Syscall)
82 * @param id ID of message queue
83 * @return Success?
85 int ipc_msg_destroy(id_t id) {
86 ipc_msg_t *msq = ipc_msg_find(-1,id);
87 if (msq!=NULL) {
88 if (perm_check(proc_current->pid,proc_current->gid,msq->ipc.owner->uid,msq->ipc.owner->gid,msq->mode,PERM_W)) {
89 ipc_msg_msg_t *msg;
90 proc_t *waiter;
91 while ((msg = llist_pop(msq->msgs))!=NULL) {
92 if ((msg->flags&IPC_NOWAIT)!=msg->flags) proc_wake(msg->sender);
93 free(msg->data);
94 free(msg);
96 while ((waiter = llist_pop(msq->waiting))!=NULL) proc_wake(waiter);
97 llist_remove(ipc_objects,llist_find(ipc_objects,msq));
98 free(msq);
99 return 0;
101 else return -EACCES;
103 return -EINVAL;
107 * Sends a message (Syscall)
108 * @param id MSQID
109 * @param msg Message data
110 * @param msgsz Message size
111 * @param type Message type
112 * @param flags Flags
113 * @param time Current time
114 * @return Success?
116 int ipc_msg_send(id_t id,void *data,size_t msgsz,long type,int flags,time_t time) {
117 proc_t *waiter;
118 ipc_msg_t *msq = ipc_msg_find(-1,id);
119 if (msq!=NULL) {
120 if (perm_check(proc_current->pid,proc_current->gid,msq->ipc.owner->uid,msq->ipc.owner->gid,msq->mode,PERM_W)) {
121 ipc_msg_msg_t *new = malloc(sizeof(ipc_msg_msg_t));
122 new->msg = msq;
123 new->data = memcpy(malloc(msgsz),data,msgsz);
124 new->type = type;
125 new->size = msgsz;
126 new->flags = flags;
127 new->sender = proc_current;
128 msq->lspid = proc_current->pid;
129 msq->stime = time;
130 llist_push(msq->msgs,new);
131 while ((waiter = llist_pop(msq->waiting))!=NULL) proc_wake(waiter);
132 if ((flags&IPC_NOWAIT)!=flags) proc_sleep(proc_current);
133 return 0;
135 else return -EACCES;
137 return -EINVAL;
141 * Receives a message (Syscall)
142 * @param id MSQID
143 * @param data Buffer for message data
144 * @param msgsz Size of buffer
145 * @param type Type message must have
146 * @param flags Flags
147 * @param time Current time
148 * @return Number of bytes received
149 * @todo Get first message with type, not first absolute message
151 ssize_t ipc_msg_recv(id_t id,void *data,size_t msgsz,long type,int flags,time_t time) {
152 ipc_msg_t *msq = ipc_msg_find(-1,id);
153 if (msq!=NULL) {
154 if (perm_check(proc_current->pid,proc_current->gid,msq->ipc.owner->uid,msq->ipc.owner->gid,msq->mode,PERM_R)) {
155 ipc_msg_msg_t *msg;
156 size_t i;
157 for (i=0;(msg = llist_get(msq->msgs,i));i++) {
158 if (msg->type==type || type==0 || (type<0 && msg->type<=-type)) {
159 size_t count = msgsz>msg->size?msg->size:msgsz;
160 memcpy(data,msg->data,count);
161 if ((msg->flags&IPC_NOWAIT)==flags) proc_wake(msg->sender);
162 msq->lrpid = proc_current->pid;
163 msq->rtime = time;
164 free(msg->data);
165 free(msg);
166 return count;
169 if (!(flags&IPC_NOWAIT)) {
170 llist_push(msq->waiting,proc_current);
171 proc_sleep(proc_current);
172 return 0; // try again after sleeping
174 return -ENOMSG;
176 else return -EACCES;
178 return -EINVAL;
182 * Gets information about a message queue (Syscall)
183 * @param id MSQID
184 * @param uid Reference for owner's UID
185 * @param gid Reference for owner's GID
186 * @param cuid Reference for creator's UID
187 * @param cgid Reference for creator's GID
188 * @param mode Reference for mode
189 * @param num Reference for number of messages
190 * @param lspid Reference for last sender's PID
191 * @param lrpid Reference for last receiver's PID
192 * @param stime Reference for last send time
193 * @param rtime Reference for last receive time
194 * @param ctime Reference for last change time
195 * @return Success?
197 int msg_stat(id_t id,uid_t *uid,gid_t *gid,uid_t *cuid,gid_t *cgid,mode_t *mode,size_t *num,pid_t *lspid,pid_t *lrpid,time_t *stime,time_t *rtime,time_t *ctime) {
198 ipc_msg_t *msq = ipc_msg_find(-1,id);
199 if (msq!=NULL) {
200 *uid = msq->ipc.owner->uid;
201 *gid = msq->ipc.owner->gid;
202 *cuid = msq->ipc.creator->uid;
203 *cgid = msq->ipc.creator->gid;
204 *mode = msq->mode;
205 *num = llist_size(msq->msgs);
206 *lspid = msq->lspid;
207 *lrpid = msq->lrpid;
208 *stime = msq->stime;
209 *rtime = msq->rtime;
210 *ctime = msq->ctime;
211 return 0;
213 else return -EINVAL;
217 * Changes information of a message queue (Syscall)
218 * @param id MSQID
219 * @param uid Owner's UID
220 * @param gid Owner's GID
221 * @param mode Permissions
222 * @param time Current time
223 * @return Success?
225 int msg_set(id_t id,uid_t uid,gid_t gid,mode_t mode,time_t time) {
226 ipc_msg_t *msq = ipc_msg_find(-1,id);
227 if (msq!=NULL) {
228 if (perm_check(proc_current->pid,proc_current->gid,msq->ipc.owner->uid,msq->ipc.owner->gid,msq->mode,PERM_W) || proc_current==msq->ipc.creator) {
229 msq->mode = mode;
230 msq->ctime = time;
232 return 0;
234 return -EINVAL;