xref: /dragonfly/usr.sbin/sysvipcd/shmd.c (revision e6d22e9b)
1 /*
2  * Copyright (c) 1994 Adam Glass and Charles Hannum.  All rights reserved.
3  * Copyright (c) 2013 Larisa Grigore <larisagrigore@gmail.com>
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *	This product includes software developed by Adam Glass and Charles
16  *	Hannum.
17  * 4. The names of the authors may not be used to endorse or promote products
18  *    derived from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
21  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23  * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #include <sys/param.h>
35 #include <sys/vmmeter.h>
36 #include <sys/wait.h>
37 #include <sys/queue.h>
38 #include <sys/mman.h>
39 
40 #include <err.h>
41 #include <errno.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <unistd.h>
46 #include <fcntl.h>
47 #include <time.h>
48 
49 #include "limits.h"
50 #include "perm.h"
51 #include "utilsd.h"
52 
53 #include "shmd.h"
54 #include "sysvipc_hash.h"
55 #include "sysvipc_sockets.h"
56 
57 static struct	shminfo shminfo = {
58 //	0,
59 	SHMMIN,
60 	SHMMNI,
61 	SHMSEG
62 //	0
63 };
64 
65 /* Shared memory.*/
66 static int shm_last_free, shm_committed, shmalloced;
67 int shm_nused;
68 static struct shmid_ds	*shmsegs;
69 
70 /* Message queues.*/
71 extern struct msginfo msginfo;
72 
73 extern struct hashtable *clientshash;
74 
75 static int
76 create_sysv_file(struct shmget_msg *msg, size_t size,
77 		struct shmid_ds *shmseg) {
78 	char filename[FILENAME_MAX];
79 	int fd;
80 	void *addr;
81 	int nsems;
82 	struct semid_pool *sems;
83 	struct msqid_pool *msgq;
84 	key_t key = msg->key;
85 	int i;
86 
87 	errno = 0;
88 
89 	switch(msg->type) {
90 		case SHMGET:
91 			sprintf(filename, "%s/%s_%ld", DIRPATH, SHM_NAME, key);
92 			break;
93 		case SEMGET:
94 			sprintf(filename, "%s/%s_%ld", DIRPATH, SEM_NAME, key);
95 			break;
96 		case MSGGET:
97 			sprintf(filename, "%s/%s_%ld", DIRPATH, MSG_NAME, key);
98 			break;
99 		case UNDOGET:
100 			sprintf(filename, "%s/%s_%ld", DIRPATH, UNDO_NAME, key);
101 			break;
102 		default:
103 			return (-EINVAL);
104 	}
105 
106 	fd = open(filename, O_RDWR | O_CREAT, 0666);
107 	if (fd < 0) {
108 		sysvd_print_err("create sysv file: open\n");
109 		goto out;
110 	}
111 
112 	ftruncate(fd, size);
113 
114 	switch(msg->type) {
115 		case SEMGET:
116 			/* Map the semaphore to initialize it. */
117 			addr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
118 			//TODO modify 0 for more sems on a page
119 			if (!addr) {
120 				sysvd_print_err("create sysv file: mmap");
121 				goto error;
122 			}
123 
124 			/* There is no need for any lock because all clients
125 			 * that try to access this segment are blocked until
126 			 * it becames ~SHMSEG_REMOVED. */
127 			sems = (struct semid_pool*)addr;
128 			nsems = (msg->size - sizeof(struct semid_pool)) /
129 				sizeof(struct sem);
130 			sysvd_print("allocate %d sems\n", nsems);
131 
132 			/* Init lock. */
133 #ifdef SYSV_RWLOCK
134 			sysv_rwlock_init(&sems->rwlock);
135 #else
136 			sysv_mutex_init(&sems->mutex);
137 #endif
138 			/* Credentials are kept in shmid_ds structure. */
139 			sems->ds.sem_perm.seq = shmseg->shm_perm.seq;
140 			sems->ds.sem_nsems = nsems;
141 			sems->ds.sem_otime = 0;
142 			//sems->ds.sem_ctime = time(NULL);
143 			//semtot += nsems;
144 			sems->gen = 0;
145 
146 			/* Initialize each sem. */
147 			memset(sems->ds.sem_base, 0, nsems + sizeof(struct sem));
148 
149 #ifdef SYSV_SEMS
150 			int l;
151 			for (l=0; l < nsems; l++)
152 				sysv_mutex_init(&sems->ds.sem_base[l].sem_mutex);
153 #endif
154 
155 			munmap(addr, size);
156 
157 			break;
158 		case MSGGET:
159 			/* Map the message queue to initialize it. */
160 			addr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
161 			if (!addr) {
162 				sysvd_print_err("create sysv file: mmap");
163 				goto error;
164 			}
165 
166 			/* There is no need for any lock because all clients
167 			 * that try to access this segment are blocked until
168 			 * it becames ~SHMSEG_REMOVED. */
169 			msgq = (struct msqid_pool*)addr; //TODO
170 			/*sysvd_print("Attention!!! : %ld %ld %ld %ld\n",
171 					sizeof(struct msqid_pool),
172 					sizeof(msgq->msghdrs),
173 					sizeof(msgq->msgmaps),
174 					sizeof(msgq->msgpool));*/
175 
176 			/* Init lock. */
177 #ifdef SYSV_RWLOCK
178 			sysv_rwlock_init(&msgq->rwlock);
179 #else
180 			sysv_mutex_init(&msgq->mutex);
181 #endif
182 			/* In kernel implementation, this was done globally. */
183 			for (i = 0; i < msginfo.msgseg; i++) {
184 				if (i > 0)
185 					msgq->msgmaps[i-1].next = i;
186 				msgq->msgmaps[i].next = -1;	/* implies entry is available */
187 			}
188 			msgq->free_msgmaps = 0;
189 			msgq->nfree_msgmaps = msginfo.msgseg;
190 
191 			for (i = 0; i < msginfo.msgtql; i++) {
192 				msgq->msghdrs[i].msg_type = 0;
193 				if (i > 0)
194 					msgq->msghdrs[i-1].msg_next = i;
195 				msgq->msghdrs[i].msg_next = -1;
196 			}
197 			msgq->free_msghdrs = 0;
198 
199 			/* Credentials are kept in shmid_ds structure. */
200 			msgq->ds.msg_perm.seq = shmseg->shm_perm.seq;
201 			msgq->ds.first.msg_first_index = -1;
202 			msgq->ds.last.msg_last_index = -1;
203 			msgq->ds.msg_cbytes = 0;
204 			msgq->ds.msg_qnum = 0;
205 			msgq->ds.msg_qbytes = msginfo.msgmnb;
206 			msgq->ds.msg_lspid = 0;
207 			msgq->ds.msg_lrpid = 0;
208 			msgq->ds.msg_stime = 0;
209 			msgq->ds.msg_rtime = 0;
210 
211 			munmap(addr, size);
212 
213 			break;
214 		default:
215 			break;
216 	}
217 
218 	unlink(filename);
219 out:
220 	return (fd);
221 error:
222 	close(fd);
223 	return (-1);
224 }
225 
226 /* Install for the client the file corresponding to fd. */
227 static int
228 install_fd_client(pid_t pid, int fd) {
229 	int ret;
230 	struct client *cl = _hash_lookup(clientshash, pid);
231 	if (!cl) {
232 		sysvd_print_err("no client entry for pid = %d\n", pid);
233 		return (-1);
234 	}
235 
236 	ret = send_fd(cl->sock, fd);
237 	if (ret < 0) {
238 		sysvd_print_err("can not send fd to client %d\n", pid);
239 		return (-1);
240 	}
241 
242 	return (0);
243 }
244 
245 static int
246 shm_find_segment_by_key(key_t key)
247 {
248 	int i;
249 
250 	for (i = 0; i < shmalloced; i++) {
251 		if ((shmsegs[i].shm_perm.mode & SHMSEG_ALLOCATED) &&
252 				shmsegs[i].shm_perm.key == key)
253 			return (i);
254 	}
255 	return (-1);
256 }
257 
258 static struct shmid_ds *
259 shm_find_segment_by_shmid(int shmid)
260 {
261 	int segnum;
262 	struct shmid_ds *shmseg;
263 
264 	segnum = IPCID_TO_IX(shmid);
265 	if (segnum < 0 || segnum >= shmalloced) {
266 		sysvd_print_err("segnum out of range\n");
267 		return (NULL);
268 	}
269 
270 	shmseg = &shmsegs[segnum];
271 	if ((shmseg->shm_perm.mode & (SHMSEG_ALLOCATED | SHMSEG_REMOVED))
272 			!= SHMSEG_ALLOCATED ||
273 			shmseg->shm_perm.seq != IPCID_TO_SEQ(shmid)) {
274 		sysvd_print("segment most probably removed\n");
275 		return (NULL);
276 	}
277 	return (shmseg);
278 }
279 
280 /* Remove a shared memory segment. */
281 static void
282 shm_deallocate_segment(int segnum)
283 {
284 	size_t size;
285 	struct shmid_ds *shmseg = &shmsegs[segnum];
286 	struct shm_handle *internal =
287 		(struct shm_handle *)shmseg->shm_internal;
288 //	int nsems;
289 
290 	sysvd_print("deallocate segment %d\n", segnum);
291 
292 	size = round_page(shmseg->shm_segsz);
293 
294 #if 0
295 	if (internal->type == SEMGET) {
296 			nsems = (shmseg->shm_segsz - sizeof(struct semid_pool)) /
297 				sizeof(struct sem);
298 			semtot -= nsems;
299 			sysvd_print("freed %d sems\n", nsems);
300 	}
301 #endif
302 
303 	/* Close the corresponding file. */
304 	close(internal->fd);
305 
306 	/* Free other resources. */
307 	free(shmseg->shm_internal);
308 	shmseg->shm_internal = NULL;
309 	shm_committed -= btoc(size);
310 	shm_nused--;
311 
312 	shmseg->shm_perm.mode = SHMSEG_FREE;
313 }
314 
315 static void *map_seg(int);
316 static int munmap_seg(int, void *);
317 
318 /* In sem and msg case notify the other processes that use it. */
319 static void
320 mark_segment_removed(int shmid, int type) {
321 	struct semid_pool *semaptr;
322 	struct msqid_pool *msgq;
323 
324 	switch (type) {
325 		case SEMGET:
326 			semaptr = (struct semid_pool *)map_seg(shmid);
327 #ifdef SYSV_RWLOCK
328 			sysv_rwlock_wrlock(&semaptr->rwlock);
329 #else
330 			sysv_mutex_lock(&semaptr->mutex);
331 #endif
332 			semaptr->gen = -1;
333 
334 			/* It is not necessary to wake waiting threads because
335 			 * if the group of semaphores is acquired by a thread,
336 			 * the smaptr lock is held, so it is impossible to
337 			 * reach this point.
338 			 */
339 #ifdef SYSV_RWLOCK
340 			sysv_rwlock_unlock(&semaptr->rwlock);
341 #else
342 			sysv_mutex_unlock(&semaptr->mutex);
343 #endif
344 			munmap_seg(shmid, semaptr);
345 			break;
346 		case MSGGET :
347 			msgq = (struct msqid_pool*)map_seg(shmid);
348 #ifdef SYSV_RWLOCK
349 			sysv_rwlock_wrlock(&msgq->rwlock);
350 #else
351 			sysv_mutex_lock(&msgq->mutex);
352 #endif
353 			msgq->gen = -1;
354 
355 #ifdef SYSV_RWLOCK
356 			sysv_rwlock_unlock(&msgq->rwlock);
357 #else
358 			sysv_mutex_unlock(&msgq->mutex);
359 #endif
360 			munmap_seg(shmid, msgq);
361 			break;
362 		default:
363 			break;
364 	}
365 }
366 
367 /* Get the id of an existing shared memory segment. */
368 static int
369 shmget_existing(struct shmget_msg *shmget_msg, int mode,
370 		int segnum, struct cmsgcred *cred)
371 {
372 	struct shmid_ds *shmseg;
373 	int error;
374 
375 	shmseg = &shmsegs[segnum];
376 	if (shmseg->shm_perm.mode & SHMSEG_REMOVED) {
377 		/*
378 		 * This segment is in the process of being allocated.  Wait
379 		 * until it's done, and look the key up again (in case the
380 		 * allocation failed or it was freed).
381 		 */
382 		//TODO Maybe it will be necessary if the daemon is multithreading
383 		/*shmseg->shm_perm.mode |= SHMSEG_WANTED;
384 		  error = tsleep((caddr_t)shmseg, PCATCH, "shmget", 0);
385 		  if (error)
386 		  return error;
387 		  return EAGAIN;*/
388 	}
389 	if ((shmget_msg->shmflg & (IPC_CREAT | IPC_EXCL)) == (IPC_CREAT | IPC_EXCL))
390 		return (-EEXIST);
391 	error = ipcperm(cred, &shmseg->shm_perm, mode);
392 	if (error)
393 		return (-error);
394 	if (shmget_msg->size && (shmget_msg->size > shmseg->shm_segsz))
395 		return (-EINVAL);
396 	return (IXSEQ_TO_IPCID(segnum, shmseg->shm_perm));
397 }
398 
399 /* Create a shared memory segment and return the id. */
400 static int
401 shmget_allocate_segment(pid_t pid, struct shmget_msg *shmget_msg,
402 		int mode, struct cmsgcred *cred)
403 {
404 	int i, segnum, shmid;
405 	size_t size;
406 	struct shmid_ds *shmseg;
407 	struct shm_handle *handle;
408 #if 0
409 	/* It is possible after a process calls exec().
410 	 * We don't create another segment but return the old one
411 	 * with all information.
412 	 * This segment is destroyed only when process dies.
413 	 * */
414 	if (shmget_msg->type == UNDOGET) {
415 		struct client *cl= _hash_lookup(clientshash, pid);
416 		if (cl->undoid != -1)
417 			return cl->undoid;
418 	}
419 #endif
420 	if ((long)shmget_msg->size < shminfo.shmmin)
421 			//|| (long)shmget_msg->size > shminfo.shmmax)
422 			/* There is no need to check the max limit,
423 			 * the operating system do this for us.
424 			 */
425 		return (-EINVAL);
426 	if (shm_nused >= shminfo.shmmni) /* any shmids left? */
427 		return (-ENOSPC);
428 
429 	/* Compute the size of the segment. */
430 	size = round_page(shmget_msg->size);
431 
432 	/* Find a free entry in the shmsegs vector. */
433 	if (shm_last_free < 0) {
434 		//	shmrealloc();	/* maybe expand the shmsegs[] array */
435 		for (i = 0; i < shmalloced; i++) {
436 			if (shmsegs[i].shm_perm.mode & SHMSEG_FREE)
437 				break;
438 		}
439 		if (i == shmalloced) {
440 			sysvd_print("i == shmalloced\n");
441 			return (-ENOSPC);
442 		}
443 		segnum = i;
444 	} else  {
445 		segnum = shm_last_free;
446 		shm_last_free = -1;
447 	}
448 	shmseg = &shmsegs[segnum];
449 	/*
450 	 * In case we sleep in malloc(), mark the segment present but deleted
451 	 * so that noone else tries to create the same key.
452 	 */
453 	shmseg->shm_perm.mode = SHMSEG_ALLOCATED | SHMSEG_REMOVED;
454 	shmseg->shm_perm.key = shmget_msg->key;
455 	shmseg->shm_perm.seq = (shmseg->shm_perm.seq + 1) & 0x7fff;
456 
457 	/* Create the file for the shared memory segment. */
458 	handle = shmseg->shm_internal = malloc(sizeof(struct shm_handle));
459 	handle->type = shmget_msg->type;
460 	handle->fd = create_sysv_file(shmget_msg, size, shmseg);
461 	if (handle->fd == -1) {
462 		free(handle);
463 		handle = NULL;
464 		shmseg->shm_perm.mode = SHMSEG_FREE;
465 		shm_last_free = segnum;
466 		errno = -ENFILE;
467 		return (-1);
468 	}
469 
470 	LIST_INIT(&handle->attached_list);
471 
472 	if (handle->fd < 0) {
473 		free(shmseg->shm_internal);
474 		shmseg->shm_internal = NULL;
475 		shm_last_free = segnum;
476 		shmseg->shm_perm.mode = SHMSEG_FREE;
477 		return (-errno);
478 	}
479 
480 	/* Get the id. */
481 	shmid = IXSEQ_TO_IPCID(segnum, shmseg->shm_perm);
482 
483 	shmseg->shm_perm.cuid = shmseg->shm_perm.uid = cred->cmcred_euid;
484 	shmseg->shm_perm.cgid = shmseg->shm_perm.gid = cred->cmcred_gid;
485 	shmseg->shm_perm.mode = (shmseg->shm_perm.mode & SHMSEG_WANTED) |
486 		(mode & ACCESSPERMS) | SHMSEG_ALLOCATED;
487 
488 	shmseg->shm_cpid = pid;
489 	shmseg->shm_lpid = shmseg->shm_nattch = 0;
490 	shmseg->shm_atime = shmseg->shm_dtime = 0;
491 	shmseg->shm_ctime = time(NULL);
492 
493 	shmseg->shm_segsz = shmget_msg->size;
494 	shm_committed += btoc(size);
495 	shm_nused++;
496 
497 	if (shmseg->shm_perm.mode & SHMSEG_WANTED) {
498 		/*
499 		 * Somebody else wanted this key while we were asleep.  Wake
500 		 * them up now.
501 		 */
502 		shmseg->shm_perm.mode &= ~SHMSEG_WANTED;
503 		//TODO multithreading
504 		//wakeup((caddr_t)shmseg);
505 	}
506 	shmseg->shm_perm.mode &= ~SHMSEG_REMOVED;
507 
508 	if (shmget_msg->type == UNDOGET) {
509 		/* The file is used by daemon when clients terminates
510 		 * and sem_undo resources must be cleaned.
511 		 */
512 		struct client *cl= _hash_lookup(clientshash, pid);
513 		cl->undoid = shmid;
514 	}
515 
516 	return (shmid);
517 }
518 
519 /* Handle a shmget() request. */
520 int
521 handle_shmget(pid_t pid, struct shmget_msg *shmget_msg,
522 		struct cmsgcred *cred ) {
523 	int segnum, mode, error;
524 	struct shmid_ds *shmseg;
525 	struct shm_handle *handle;
526 
527 	//if (!jail_sysvipc_allowed && td->td_cmsgcred->cr_prison != NULL)
528 	//	return (ENOSYS);
529 	mode = shmget_msg->shmflg & ACCESSPERMS;
530 
531 	sysvd_print("ask for key = %ld\n", shmget_msg->key);
532 	shmget_msg->key = (shmget_msg->key & 0x3FFF) |
533 		(shmget_msg->type << 30);
534 	sysvd_print("ask for key = %ld\n", shmget_msg->key);
535 
536 	if (shmget_msg->key != IPC_PRIVATE) {
537 		//again:
538 		segnum = shm_find_segment_by_key(shmget_msg->key);
539 		if (segnum >= 0) {
540 			error = shmget_existing(shmget_msg, mode, segnum, cred);
541 			//TODO if daemon is multithreading
542 			//if (error == EAGAIN)
543 			//	goto again;
544 			goto done;
545 		}
546 		if ((shmget_msg->shmflg & IPC_CREAT) == 0) {
547 			error = -ENOENT;
548 			goto done_err;
549 		}
550 	}
551 	error = shmget_allocate_segment(pid, shmget_msg, mode, cred);
552 	sysvd_print("allocate segment = %d\n", error);
553 done:
554 	/*
555 	 * Install to th client the file corresponding to the
556 	 * shared memory segment.
557 	 * client_fd is the file descriptor added in the client
558 	 * files table.
559 	 */
560 	shmseg = shm_find_segment_by_shmid(error);
561 	if (shmseg == NULL) {
562 		sysvd_print_err("can not find segment by shmid\n");
563 		return (-1);
564 	}
565 
566 	handle = (struct shm_handle *)shmseg->shm_internal;
567 	if (install_fd_client(pid, handle->fd) != 0)
568 		error = errno;
569 done_err:
570 	return (error);
571 
572 }
573 
574 /* Handle a shmat() request. */
575 int
576 handle_shmat(pid_t pid, struct shmat_msg *shmat_msg,
577 		struct cmsgcred *cred ) {
578 	int error;
579 	int fd;
580 	struct shmid_ds *shmseg;
581 	struct pid_attached *pidatt;
582 	struct shm_handle *handle;
583 	size_t new_size = shmat_msg->size;
584 	struct client *cl;
585 	struct id_attached *idatt;
586 
587 	/*if (!jail_sysvipc_allowed && td->td_cmsgcred->cr_prison != NULL)
588 	  return (ENOSYS);
589 
590 again:*/
591 	shmseg = shm_find_segment_by_shmid(shmat_msg->shmid);
592 	if (shmseg == NULL) {
593 		sysvd_print_err("shmat error: segment was not found\n");
594 		error = EINVAL;
595 		goto done;
596 	}
597 	error = ipcperm(cred, &shmseg->shm_perm,
598 			(shmat_msg->shmflg & SHM_RDONLY) ? IPC_R : IPC_R|IPC_W);
599 	if (error)
600 		goto done;
601 
602 	handle = shmseg->shm_internal;
603 
604 	if (shmat_msg->size > shmseg->shm_segsz) {
605 		if (handle->type != UNDOGET) {
606 			error = EINVAL;
607 			goto done;
608 		}
609 
610 		fd = ((struct shm_handle*)shmseg->shm_internal)->fd;
611 		ftruncate(fd, round_page(new_size));
612 		shmseg->shm_segsz = new_size;
613 	}
614 
615 	shmseg->shm_lpid = pid;
616 	shmseg->shm_atime = time(NULL);
617 
618 	if (handle->type != UNDOGET)
619 		shmseg->shm_nattch++;
620 	else
621 		shmseg->shm_nattch = 1; /* Only a process calls shmat and
622 		only once. If it does it for more than once that is because
623 		it called exec() and reinitialized the undo segment. */
624 
625 	/* Insert the pid in the segment list of attaced pids.
626 	 * The list is checked in handle_shmdt so that only
627 	 * attached pids can dettached from this segment.
628 	 */
629 	sysvd_print("nattch = %d pid = %d\n",
630 			shmseg->shm_nattch, pid);
631 
632 	pidatt = malloc(sizeof(*pidatt));
633 	pidatt->pid = pid;
634 	LIST_INSERT_HEAD(&handle->attached_list, pidatt, link);
635 
636 	/* Add the segment at the list of attached segments of the client.
637 	 * It is used when the process finishes its execution. The daemon
638 	 * walks through the list to dettach the segments.
639 	 */
640 	idatt = malloc(sizeof(*idatt));
641 	idatt->shmid = shmat_msg->shmid;
642 	cl = _hash_lookup(clientshash, pid);
643 	LIST_INSERT_HEAD(&cl->ids_attached, idatt, link);
644 
645 	return (0);
646 done:
647 	return (error);
648 }
649 
650 /* Handle a shmdt() request. */
651 int
652 handle_shmdt(pid_t pid, int shmid) {
653 	struct shmid_ds *shmseg;
654 	int segnum;
655 	struct shm_handle *handle;
656 	struct pid_attached *pidatt;
657 	struct id_attached *idatt;
658 	struct client *cl;
659 
660 	sysvd_print("shmdt pid %d shmid %d\n", pid, shmid);
661 	/*if (!jail_sysvipc_allowed && td->td_cmsgcred->cr_prison != NULL)
662 	  return (ENOSYS);
663 	*/
664 
665 	segnum = IPCID_TO_IX(shmid);
666 	shmseg = &shmsegs[segnum];
667 	handle = shmseg->shm_internal;
668 
669 	/* Check if pid is attached. */
670 	LIST_FOREACH(pidatt, &handle->attached_list, link)
671 		if (pidatt->pid == pid)
672 			break;
673 	if (!pidatt) {
674 		sysvd_print_err("process %d is not attached to %d (1)\n",
675 				pid, shmid);
676 		return (EINVAL);
677 	}
678 	LIST_REMOVE(pidatt, link);
679 
680 	/* Remove the segment from the list of attached segments of the pid.*/
681 	cl = _hash_lookup(clientshash, pid);
682 	LIST_FOREACH(idatt, &cl->ids_attached, link)
683 		if (idatt->shmid == shmid)
684 			break;
685 	if (!idatt) {
686 		sysvd_print_err("process %d is not attached to %d (2)\n",
687 				pid, shmid);
688 		return (EINVAL);
689 	}
690 	LIST_REMOVE(idatt, link);
691 
692 	shmseg->shm_dtime = time(NULL);
693 
694 	/* If no other process attaced remove the segment. */
695 	if ((--shmseg->shm_nattch <= 0) &&
696 			(shmseg->shm_perm.mode & SHMSEG_REMOVED)) {
697 		shm_deallocate_segment(segnum);
698 		shm_last_free = segnum;
699 	}
700 
701 	return (0);
702 }
703 
704 /* Handle a shmctl() request. */
705 int
706 handle_shmctl(struct shmctl_msg *shmctl_msg,
707 		struct cmsgcred *cred ) {
708 	int error = 0;
709 	struct shmid_ds *shmseg, *inbuf;
710 
711 	/*	if (!jail_sysvipc_allowed && td->td_cmsgcred->cr_prison != NULL)
712 		return (ENOSYS);
713 		*/
714 	shmseg = shm_find_segment_by_shmid(shmctl_msg->shmid);
715 
716 	if (shmseg == NULL) {
717 		error = EINVAL;
718 		goto done;
719 	}
720 
721 	switch (shmctl_msg->cmd) {
722 		case IPC_STAT:
723 			sysvd_print("IPC STAT\n");
724 			error = ipcperm(cred, &shmseg->shm_perm, IPC_R);
725 			if (error) {
726 				sysvd_print("IPC_STAT not allowed\n");
727 				break;
728 			}
729 			shmctl_msg->buf = *shmseg;
730 			break;
731 		case IPC_SET:
732 			sysvd_print("IPC SET\n");
733 			error = ipcperm(cred, &shmseg->shm_perm, IPC_M);
734 			if (error) {
735 				sysvd_print("IPC_SET not allowed\n");
736 				break;
737 			}
738 			inbuf = &shmctl_msg->buf;
739 
740 			shmseg->shm_perm.uid = inbuf->shm_perm.uid;
741 			shmseg->shm_perm.gid = inbuf->shm_perm.gid;
742 			shmseg->shm_perm.mode =
743 				(shmseg->shm_perm.mode & ~ACCESSPERMS) |
744 				(inbuf->shm_perm.mode & ACCESSPERMS);
745 			shmseg->shm_ctime = time(NULL);
746 			break;
747 		case IPC_RMID:
748 			sysvd_print("IPC RMID shmid = %d\n",
749 					shmctl_msg->shmid);
750 			error = ipcperm(cred, &shmseg->shm_perm, IPC_M);
751 			if (error) {
752 				sysvd_print("IPC_RMID not allowed\n");
753 				break;
754 			}
755 			shmseg->shm_perm.key = IPC_PRIVATE;
756 			shmseg->shm_perm.mode |= SHMSEG_REMOVED;
757 			if (shmseg->shm_nattch <= 0) {
758 				shm_deallocate_segment(IPCID_TO_IX(shmctl_msg->shmid));
759 				shm_last_free = IPCID_TO_IX(shmctl_msg->shmid);
760 			}
761 			else {
762 				/* In sem and msg cases, other process must be
763 				 * noticed about the removal. */
764 				struct shm_handle *internal =
765 					(struct shm_handle *)shmseg->shm_internal;
766 				mark_segment_removed(shmctl_msg->shmid,
767 						internal->type);
768 			}
769 			break;
770 #if 0
771 		case SHM_LOCK:
772 		case SHM_UNLOCK:
773 #endif
774 		default:
775 			error = EINVAL;
776 			break;
777 	}
778 done:
779 	return (error);
780 
781 }
782 
783 /* Function used by daemon to map a sysv resource. */
784 static void *
785 map_seg(int shmid) {
786 	struct shmid_ds *shmseg;
787 	struct shm_handle *internal;
788 
789 	int fd;
790 	size_t size;
791 	void *addr;
792 
793 	shmseg = shm_find_segment_by_shmid(shmid);
794 	if (!shmseg) {
795 		sysvd_print_err("map_seg error:"
796 				"semid %d not found\n", shmid);
797 		return (NULL);
798 	}
799 
800 	internal = (struct shm_handle *)shmseg->shm_internal;
801 	if (!internal) {
802 		sysvd_print_err("map_seg error: internal for"
803 				"semid %d not found\n", shmid);
804 		return (NULL);
805 	}
806 
807 	fd = internal->fd;
808 
809 	size = round_page(shmseg->shm_segsz);
810 
811 	addr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
812 	if (!addr) {
813 		sysvd_print_err("map_seg: error mmap semid = %d\n", shmid);
814 		return (NULL);
815 	}
816 
817 	return (addr);
818 }
819 
820 /* Function used by daemon to munmap a sysv resource. */
821 static int
822 munmap_seg(int shmid, void *addr) {
823 	struct shmid_ds *shmseg;
824 	struct shm_handle *internal;
825 
826 	size_t size;
827 
828 	shmseg = shm_find_segment_by_shmid(shmid);
829 	if (!shmseg) {
830 		sysvd_print_err("munmap_seg error:"
831 				"semid %d not found\n", shmid);
832 		return (-1);
833 	}
834 
835 	internal = (struct shm_handle *)shmseg->shm_internal;
836 	if (!internal) {
837 		sysvd_print_err("munmap_seg error: internal for"
838 				"semid %d not found\n", shmid);
839 		return (-1);
840 	}
841 
842 	size = round_page(shmseg->shm_segsz);
843 	munmap(addr, size);
844 
845 	return (0);
846 }
847 
848 void
849 shminit(void) {
850 	int i;
851 
852 	shmalloced = shminfo.shmmni;
853 	shmsegs = malloc(shmalloced * sizeof(shmsegs[0]));
854 	for (i = 0; i < shmalloced; i++) {
855 		shmsegs[i].shm_perm.mode = SHMSEG_FREE;
856 		shmsegs[i].shm_perm.seq = 0;
857 	}
858 	shm_last_free = 0;
859 	shm_nused = 0;
860 	shm_committed = 0;
861 
862 	/*
863 	 * msginfo.msgssz should be a power of two for efficiency reasons.
864 	 * It is also pretty silly if msginfo.msgssz is less than 8
865 	 * or greater than about 256 so ...
866 	 */
867 	i = 8;
868 	while (i < 1024 && i != msginfo.msgssz)
869 		i <<= 1;
870 	if (i != msginfo.msgssz) {
871 		sysvd_print_err("msginfo.msgssz=%d (0x%x)\n", msginfo.msgssz,
872 		    msginfo.msgssz);
873 		sysvd_print_err("msginfo.msgssz not a small power of 2");
874 		exit(-1);
875 	}
876 	msginfo.msgmax = msginfo.msgseg * msginfo.msgssz;
877 }
878 
879 /*static void
880 shmfree(void) {
881 	free(shmsegs);
882 }*/
883 
884 int
885 semexit(int undoid) {
886 	struct sem_undo *suptr;
887 	struct sem *semptr;
888 	struct shmid_ds *undoseg;
889 
890 	if (undoid < 0) {
891 		return (-1);
892 	}
893 
894 	undoseg = shm_find_segment_by_shmid(undoid);
895 	/* The UNDO segment must be mapped by only one segment. */
896 	if (undoseg->shm_nattch != 1) {
897 		sysvd_print_err("undo segment mapped by more"
898 				"than one process\n");
899 		exit(-1);
900 	}
901 
902 	suptr = (struct sem_undo *)map_seg(undoid);
903 	if (suptr == NULL) {
904 		sysvd_print_err("no %d undo segment found\n", undoid);
905 		return (-1);
906 	}
907 
908 	/* No locking mechanism is required because only the
909 	 * client and the daemon can access the UNDO segment.
910 	 * At this moment the client is disconnected so only
911 	 * the daemon can modify this segment.
912 	 */
913 	while (suptr->un_cnt) {
914 		struct semid_pool *semaptr;
915 		int semid;
916 		int semnum;
917 		int adjval;
918 		int ix;
919 
920 		ix = suptr->un_cnt - 1;
921 		semid = suptr->un_ent[ix].un_id;
922 		semnum = suptr->un_ent[ix].un_num;
923 		adjval = suptr->un_ent[ix].un_adjval;
924 
925 		semaptr = (struct semid_pool *)map_seg(semid);
926 		if (!semaptr) {
927 			return (-1);
928 		}
929 
930 		/* Was it removed? */
931 		if (semaptr->gen == -1 ||
932 			semaptr->ds.sem_perm.seq != IPCID_TO_SEQ(semid) ||
933 			(semaptr->ds.sem_perm.mode & SHMSEG_ALLOCATED) == 0) {
934 			--suptr->un_cnt;
935 			sysvd_print_err("semexit - semid not allocated\n");
936 			continue;
937 		}
938 		if (semnum >= semaptr->ds.sem_nsems) {
939 			--suptr->un_cnt;
940 			sysvd_print_err("semexit - semnum out of range\n");
941 			continue;
942 		}
943 
944 #ifdef SYSV_RWLOCK
945 #ifdef SYSV_SEMS
946 		sysv_rwlock_rdlock(&semaptr->rwlock);
947 #else
948 		sysv_rwlock_wrlock(&semaptr->rwlock);
949 #endif //SYSV_SEMS
950 #else
951 		sysv_mutex_lock(&semaptr->mutex);
952 		/* Nobody can remove the semaphore beteen the check and the
953 		 * lock acquisition because it must first send a IPC_RMID
954 		 * to me and I will process that after finishing this function.
955 		 */
956 #endif //SYSV_RWLOCK
957 		semptr = &semaptr->ds.sem_base[semnum];
958 #ifdef SYSV_SEMS
959 		sysv_mutex_lock(&semptr->sem_mutex);
960 #endif
961 		if (ix == suptr->un_cnt - 1 &&
962 		    semid == suptr->un_ent[ix].un_id &&
963 		    semnum == suptr->un_ent[ix].un_num &&
964 		    adjval == suptr->un_ent[ix].un_adjval) {
965 			--suptr->un_cnt;
966 
967 			if (adjval < 0) {
968 				if (semptr->semval < -adjval)
969 					semptr->semval = 0;
970 				else
971 					semptr->semval += adjval;
972 			} else {
973 				semptr->semval += adjval;
974 			}
975 			/* TODO multithreaded daemon:
976 			 * Check again if the semaphore was removed and do
977 			 * not wake anyone if it was.*/
978 			umtx_wakeup((int *)&semptr->semval, 0);
979 		}
980 #ifdef SYSV_SEMS
981 		sysv_mutex_unlock(&semptr->sem_mutex);
982 #endif
983 
984 #ifdef SYSV_RWLOCK
985 		sysv_rwlock_unlock(&semaptr->rwlock);
986 #else
987 		sysv_mutex_unlock(&semaptr->mutex);
988 #endif
989 		munmap_seg(semid, semaptr);
990 	}
991 
992 	munmap_seg(undoid, suptr);
993 	return (0);
994 }
995 
996 void
997 shmexit(struct client *cl) {
998 	struct id_attached *idatt;
999 
1000 	while (!LIST_EMPTY(&cl->ids_attached)) {
1001 		idatt = LIST_FIRST(&cl->ids_attached);
1002 		handle_shmdt(cl->pid, idatt->shmid);
1003 	}
1004 }
1005