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