custom message for SHMGET
[minix3.git] / servers / ipc / shm.c
blobd213301a1abe8091a45273cbf6c080f7f623fd48
1 #include "inc.h"
3 #define MAX_SHM_NR 1024
5 struct shm_struct {
6 key_t key;
7 int id;
8 struct shmid_ds shmid_ds;
9 vir_bytes page;
10 int vm_id;
12 static struct shm_struct shm_list[MAX_SHM_NR];
13 static int shm_list_nr = 0;
15 static struct shm_struct *shm_find_key(key_t key)
17 int i;
18 if (key == IPC_PRIVATE)
19 return NULL;
20 for (i = 0; i < shm_list_nr; i++)
21 if (shm_list[i].key == key)
22 return shm_list+i;
23 return NULL;
26 static struct shm_struct *shm_find_id(int id)
28 int i;
29 for (i = 0; i < shm_list_nr; i++)
30 if (shm_list[i].id == id)
31 return shm_list+i;
32 return NULL;
35 /*===========================================================================*
36 * do_shmget *
37 *===========================================================================*/
38 int do_shmget(message *m)
40 struct shm_struct *shm;
41 long key, size, old_size;
42 int flag;
43 int id;
45 key = m->m_lc_ipc_shmget.key;
46 old_size = size = m->m_lc_ipc_shmget.size;
47 flag = m->m_lc_ipc_shmget.flag;
49 if ((shm = shm_find_key(key))) {
50 if (!check_perm(&shm->shmid_ds.shm_perm, who_e, flag))
51 return EACCES;
52 if ((flag & IPC_CREAT) && (flag & IPC_EXCL))
53 return EEXIST;
54 if (size && shm->shmid_ds.shm_segsz < size)
55 return EINVAL;
56 id = shm->id;
57 } else { /* no key found */
58 if (!(flag & IPC_CREAT))
59 return ENOENT;
60 if (size <= 0)
61 return EINVAL;
62 /* round up to a multiple of PAGE_SIZE */
63 if (size % PAGE_SIZE)
64 size += PAGE_SIZE - size % PAGE_SIZE;
65 if (size <= 0)
66 return EINVAL;
68 if (shm_list_nr == MAX_SHM_NR)
69 return ENOMEM;
70 /* TODO: shmmni should be changed... */
71 if (identifier == SHMMNI)
72 return ENOSPC;
73 shm = &shm_list[shm_list_nr];
74 memset(shm, 0, sizeof(struct shm_struct));
75 shm->page = (vir_bytes) mmap(0, size,
76 PROT_READ|PROT_WRITE, MAP_ANON, -1, 0);
77 if (shm->page == (vir_bytes) MAP_FAILED)
78 return ENOMEM;
79 shm->vm_id = vm_getphys(sef_self(), (void *) shm->page);
80 memset((void *)shm->page, 0, size);
82 shm->shmid_ds.shm_perm.cuid =
83 shm->shmid_ds.shm_perm.uid = getnuid(who_e);
84 shm->shmid_ds.shm_perm.cgid =
85 shm->shmid_ds.shm_perm.gid = getngid(who_e);
86 shm->shmid_ds.shm_perm.mode = flag & 0777;
87 shm->shmid_ds.shm_segsz = old_size;
88 shm->shmid_ds.shm_atime = 0;
89 shm->shmid_ds.shm_dtime = 0;
90 shm->shmid_ds.shm_ctime = time(NULL);
91 shm->shmid_ds.shm_cpid = getnpid(who_e);
92 shm->shmid_ds.shm_lpid = 0;
93 shm->shmid_ds.shm_nattch = 0;
94 shm->id = id = identifier++;
95 shm->key = key;
97 shm_list_nr++;
100 m->m_lc_ipc_shmget.retid = id;
101 return OK;
104 /*===========================================================================*
105 * do_shmat *
106 *===========================================================================*/
107 int do_shmat(message *m)
109 int id, flag;
110 vir_bytes addr;
111 void *ret;
112 struct shm_struct *shm;
114 id = m->SHMAT_ID;
115 addr = (vir_bytes) m->SHMAT_ADDR;
116 flag = m->SHMAT_FLAG;
118 if (addr && (addr % PAGE_SIZE)) {
119 if (flag & SHM_RND)
120 addr -= (addr % PAGE_SIZE);
121 else
122 return EINVAL;
125 if (!(shm = shm_find_id(id)))
126 return EINVAL;
128 if (flag & SHM_RDONLY)
129 flag = 0444;
130 else
131 flag = 0666;
132 if (!check_perm(&shm->shmid_ds.shm_perm, who_e, flag))
133 return EACCES;
135 ret = vm_remap(who_e, sef_self(), (void *)addr, (void *)shm->page,
136 shm->shmid_ds.shm_segsz);
137 if (ret == MAP_FAILED)
138 return ENOMEM;
140 shm->shmid_ds.shm_atime = time(NULL);
141 shm->shmid_ds.shm_lpid = getnpid(who_e);
142 /* nattach is updated lazily */
144 m->SHMAT_RETADDR = (long) ret;
145 return OK;
148 /*===========================================================================*
149 * update_refcount_and_destroy *
150 *===========================================================================*/
151 void update_refcount_and_destroy(void)
153 int i, j;
155 for (i = 0, j = 0; i < shm_list_nr; i++) {
156 u8_t rc;
158 rc = vm_getrefcount(sef_self(), (void *) shm_list[i].page);
159 if (rc == (u8_t) -1) {
160 printf("IPC: can't find physical region.\n");
161 continue;
163 shm_list[i].shmid_ds.shm_nattch = rc - 1;
165 if (shm_list[i].shmid_ds.shm_nattch ||
166 !(shm_list[i].shmid_ds.shm_perm.mode & SHM_DEST)) {
167 if (i != j)
168 shm_list[j] = shm_list[i];
169 j++;
170 } else {
171 int size = shm_list[i].shmid_ds.shm_segsz;
172 if (size % PAGE_SIZE)
173 size += PAGE_SIZE - size % PAGE_SIZE;
174 munmap((void *)shm_list[i].page, size);
177 shm_list_nr = j;
180 /*===========================================================================*
181 * do_shmdt *
182 *===========================================================================*/
183 int do_shmdt(message *m)
185 vir_bytes addr;
186 phys_bytes vm_id;
187 int i;
189 addr = m->SHMDT_ADDR;
191 if ((vm_id = vm_getphys(who_e, (void *) addr)) == 0)
192 return EINVAL;
194 for (i = 0; i < shm_list_nr; i++) {
195 if (shm_list[i].vm_id == vm_id) {
196 struct shm_struct *shm = &shm_list[i];
198 shm->shmid_ds.shm_atime = time(NULL);
199 shm->shmid_ds.shm_lpid = getnpid(who_e);
200 /* nattch is updated lazily */
202 vm_unmap(who_e, (void *) addr);
203 break;
206 if (i == shm_list_nr)
207 printf("IPC: do_shmdt impossible error! could not find id %lu to unmap\n",
208 vm_id);
210 update_refcount_and_destroy();
212 return OK;
215 /*===========================================================================*
216 * do_shmctl *
217 *===========================================================================*/
218 int do_shmctl(message *m)
220 int id = m->SHMCTL_ID;
221 int cmd = m->SHMCTL_CMD;
222 struct shmid_ds *ds = (struct shmid_ds *)m->SHMCTL_BUF;
223 struct shmid_ds tmp_ds;
224 struct shm_struct *shm = NULL;
225 struct shminfo sinfo;
226 struct shm_info s_info;
227 uid_t uid;
228 int r, i;
230 if (cmd == IPC_STAT)
231 update_refcount_and_destroy();
233 if ((cmd == IPC_STAT ||
234 cmd == IPC_SET ||
235 cmd == IPC_RMID) &&
236 !(shm = shm_find_id(id)))
237 return EINVAL;
239 switch (cmd) {
240 case IPC_STAT:
241 if (!ds)
242 return EFAULT;
243 /* check whether it has read permission */
244 if (!check_perm(&shm->shmid_ds.shm_perm, who_e, 0444))
245 return EACCES;
246 r = sys_datacopy(SELF, (vir_bytes)&shm->shmid_ds,
247 who_e, (vir_bytes)ds, sizeof(struct shmid_ds));
248 if (r != OK)
249 return EFAULT;
250 break;
251 case IPC_SET:
252 uid = getnuid(who_e);
253 if (uid != shm->shmid_ds.shm_perm.cuid &&
254 uid != shm->shmid_ds.shm_perm.uid &&
255 uid != 0)
256 return EPERM;
257 r = sys_datacopy(who_e, (vir_bytes)ds,
258 SELF, (vir_bytes)&tmp_ds, sizeof(struct shmid_ds));
259 if (r != OK)
260 return EFAULT;
261 shm->shmid_ds.shm_perm.uid = tmp_ds.shm_perm.uid;
262 shm->shmid_ds.shm_perm.gid = tmp_ds.shm_perm.gid;
263 shm->shmid_ds.shm_perm.mode &= ~0777;
264 shm->shmid_ds.shm_perm.mode |= tmp_ds.shm_perm.mode & 0666;
265 shm->shmid_ds.shm_ctime = time(NULL);
266 break;
267 case IPC_RMID:
268 uid = getnuid(who_e);
269 if (uid != shm->shmid_ds.shm_perm.cuid &&
270 uid != shm->shmid_ds.shm_perm.uid &&
271 uid != 0)
272 return EPERM;
273 shm->shmid_ds.shm_perm.mode |= SHM_DEST;
274 /* destroy if possible */
275 update_refcount_and_destroy();
276 break;
277 case IPC_INFO:
278 if (!ds)
279 return EFAULT;
280 sinfo.shmmax = (unsigned long) -1;
281 sinfo.shmmin = 1;
282 sinfo.shmmni = MAX_SHM_NR;
283 sinfo.shmseg = (unsigned long) -1;
284 sinfo.shmall = (unsigned long) -1;
285 r = sys_datacopy(SELF, (vir_bytes)&sinfo,
286 who_e, (vir_bytes)ds, sizeof(struct shminfo));
287 if (r != OK)
288 return EFAULT;
289 m->SHMCTL_RET = shm_list_nr - 1;
290 if (m->SHMCTL_RET < 0)
291 m->SHMCTL_RET = 0;
292 break;
293 case SHM_INFO:
294 if (!ds)
295 return EFAULT;
296 s_info.used_ids = shm_list_nr;
297 s_info.shm_tot = 0;
298 for (i = 0; i < shm_list_nr; i++)
299 s_info.shm_tot +=
300 shm_list[i].shmid_ds.shm_segsz/PAGE_SIZE;
301 s_info.shm_rss = s_info.shm_tot;
302 s_info.shm_swp = 0;
303 s_info.swap_attempts = 0;
304 s_info.swap_successes = 0;
305 r = sys_datacopy(SELF, (vir_bytes)&s_info,
306 who_e, (vir_bytes)ds, sizeof(struct shm_info));
307 if (r != OK)
308 return EFAULT;
309 m->SHMCTL_RET = shm_list_nr - 1;
310 if (m->SHMCTL_RET < 0)
311 m->SHMCTL_RET = 0;
312 break;
313 case SHM_STAT:
314 if (id < 0 || id >= shm_list_nr)
315 return EINVAL;
316 shm = &shm_list[id];
317 r = sys_datacopy(SELF, (vir_bytes)&shm->shmid_ds,
318 who_e, (vir_bytes)ds, sizeof(struct shmid_ds));
319 if (r != OK)
320 return EFAULT;
321 m->SHMCTL_RET = shm->id;
322 break;
323 default:
324 return EINVAL;
326 return OK;
329 #if 0
330 static void list_shm_ds(void)
332 int i;
333 printf("key\tid\tpage\n");
334 for (i = 0; i < shm_list_nr; i++)
335 printf("%ld\t%d\t%lx\n",
336 shm_list[i].key,
337 shm_list[i].id,
338 shm_list[i].page);
340 #endif
342 /*===========================================================================*
343 * is_shm_nil *
344 *===========================================================================*/
345 int is_shm_nil(void)
347 return (shm_list_nr == 0);