xref: /dflybsd-src/lib/libc/sysvipc/shm.c (revision 3f7b72606e8fadb0b2e9e32302c89298c107534b)
182657471SMarkus Pfeiffer /*
282657471SMarkus Pfeiffer  * Copyright (c) 2013 Larisa Grigore <larisagrigore@gmail.com>.
382657471SMarkus Pfeiffer  *
482657471SMarkus Pfeiffer  * Redistribution and use in source and binary forms, with or without
582657471SMarkus Pfeiffer  * modification, are permitted provided that the following conditions
682657471SMarkus Pfeiffer  * are met:
782657471SMarkus Pfeiffer  * 1. Redistributions of source code must retain the above copyright
882657471SMarkus Pfeiffer  *    notice, this list of conditions and the following disclaimer.
982657471SMarkus Pfeiffer  * 2. Redistributions in binary form must reproduce the above copyright
1082657471SMarkus Pfeiffer  *    notice, this list of conditions and the following disclaimer in the
1182657471SMarkus Pfeiffer  *    documentation and/or other materials provided with the distribution.
1282657471SMarkus Pfeiffer  * 3. All advertising materials mentioning features or use of this software
1382657471SMarkus Pfeiffer  *    must display the following acknowledgement:
1482657471SMarkus Pfeiffer  *	This product includes software developed by Adam Glass and Charles
1582657471SMarkus Pfeiffer  *	Hannum.
1682657471SMarkus Pfeiffer  * 4. The names of the authors may not be used to endorse or promote products
1782657471SMarkus Pfeiffer  *    derived from this software without specific prior written permission.
1882657471SMarkus Pfeiffer  *
1982657471SMarkus Pfeiffer  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
2082657471SMarkus Pfeiffer  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2182657471SMarkus Pfeiffer  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2282657471SMarkus Pfeiffer  * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
2382657471SMarkus Pfeiffer  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2482657471SMarkus Pfeiffer  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2582657471SMarkus Pfeiffer  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2682657471SMarkus Pfeiffer  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2782657471SMarkus Pfeiffer  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2882657471SMarkus Pfeiffer  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2982657471SMarkus Pfeiffer  */
3082657471SMarkus Pfeiffer 
3182657471SMarkus Pfeiffer #include "namespace.h"
3282657471SMarkus Pfeiffer #include <sys/param.h>
3382657471SMarkus Pfeiffer #include <sys/queue.h>
3482657471SMarkus Pfeiffer #include <sys/mman.h>
3582657471SMarkus Pfeiffer #include <sys/shm.h>
367d692414Szrj #include <stdint.h>
3782657471SMarkus Pfeiffer #include <stdio.h>
3882657471SMarkus Pfeiffer #include <stdlib.h>
3982657471SMarkus Pfeiffer #include <errno.h>
4082657471SMarkus Pfeiffer #include <fcntl.h>
4182657471SMarkus Pfeiffer #include <err.h>
4282657471SMarkus Pfeiffer #include <pthread.h>
4382657471SMarkus Pfeiffer #include <unistd.h>
4482657471SMarkus Pfeiffer #include "un-namespace.h"
4582657471SMarkus Pfeiffer 
4682657471SMarkus Pfeiffer #include "sysvipc_lock.h"
4782657471SMarkus Pfeiffer #include "sysvipc_ipc.h"
4882657471SMarkus Pfeiffer #include "sysvipc_sockets.h"
4982657471SMarkus Pfeiffer #include "sysvipc_shm.h"
5082657471SMarkus Pfeiffer #include "sysvipc_hash.h"
5182657471SMarkus Pfeiffer 
5282657471SMarkus Pfeiffer #define SYSV_MUTEX_LOCK(x)	if (__isthreaded) _pthread_mutex_lock(x)
5382657471SMarkus Pfeiffer #define SYSV_MUTEX_UNLOCK(x)	if (__isthreaded) _pthread_mutex_unlock(x)
5482657471SMarkus Pfeiffer #define SYSV_MUTEX_DESTROY(x)	if (__isthreaded) _pthread_mutex_destroy(x)
5582657471SMarkus Pfeiffer 
5682657471SMarkus Pfeiffer struct hashtable *shmres = NULL;
5782657471SMarkus Pfeiffer struct hashtable *shmaddrs = NULL;
5882657471SMarkus Pfeiffer pthread_mutex_t lock_resources = PTHREAD_MUTEX_INITIALIZER;
5982657471SMarkus Pfeiffer 
6082657471SMarkus Pfeiffer /* File descriptor used to communicate with the daemon. */
6182657471SMarkus Pfeiffer extern int daemon_fd;
6282657471SMarkus Pfeiffer /* Structure used to save semaphore operation with SEMUNDO flag. */
6382657471SMarkus Pfeiffer extern struct sem_undo *undos;
6482657471SMarkus Pfeiffer 
6582657471SMarkus Pfeiffer static int
shminit(void)66ff86f401SSascha Wildner shminit(void)
67ff86f401SSascha Wildner {
6882657471SMarkus Pfeiffer 	if (shmres) {
6982657471SMarkus Pfeiffer 		errno = EPERM;
7082657471SMarkus Pfeiffer 		return (-1);
7182657471SMarkus Pfeiffer 	}
7282657471SMarkus Pfeiffer 
7382657471SMarkus Pfeiffer 	shmres = _hash_init(MAXSIZE);
7482657471SMarkus Pfeiffer 	if (!shmres)
7582657471SMarkus Pfeiffer 		goto out_resources;
7682657471SMarkus Pfeiffer 
7782657471SMarkus Pfeiffer 	shmaddrs = _hash_init(MAXSIZE);
7882657471SMarkus Pfeiffer 	if (!shmaddrs)
7982657471SMarkus Pfeiffer 		goto out_addrs;
8082657471SMarkus Pfeiffer 
8182657471SMarkus Pfeiffer 	return 0;
8282657471SMarkus Pfeiffer 
8382657471SMarkus Pfeiffer out_addrs:
8482657471SMarkus Pfeiffer 	_hash_destroy(shmres);
8582657471SMarkus Pfeiffer out_resources:
8682657471SMarkus Pfeiffer 	return -1;
8782657471SMarkus Pfeiffer }
8882657471SMarkus Pfeiffer 
8982657471SMarkus Pfeiffer /*static int
90ff86f401SSascha Wildner shmexit(void)
91ff86f401SSascha Wildner {
9282657471SMarkus Pfeiffer 	if (!shmres)
9382657471SMarkus Pfeiffer 		return -EPERM;
9482657471SMarkus Pfeiffer 
9582657471SMarkus Pfeiffer 	_hash_destroy(shmres);
9682657471SMarkus Pfeiffer 	_hash_destroy(shmaddrs);
9782657471SMarkus Pfeiffer 	SYSV_MUTEX_DESTROY(lock_resources);
9882657471SMarkus Pfeiffer 
9982657471SMarkus Pfeiffer 	return 0;
10082657471SMarkus Pfeiffer }*/
10182657471SMarkus Pfeiffer 
10282657471SMarkus Pfeiffer /* Init sysv ipc resources and those used for shared memory. */
10382657471SMarkus Pfeiffer static int
shmcheck(void)104ff86f401SSascha Wildner shmcheck(void)
105ff86f401SSascha Wildner {
10682657471SMarkus Pfeiffer 	int ret;
10782657471SMarkus Pfeiffer 
10882657471SMarkus Pfeiffer 	/* Init sysv resources. */
10982657471SMarkus Pfeiffer 	if ((ret = sysvinit()) != 0)
11082657471SMarkus Pfeiffer 		return (ret);
11182657471SMarkus Pfeiffer 	/* Init resorces used for shared memory. */
11282657471SMarkus Pfeiffer 	if ((ret = shminit()) < 0)
11382657471SMarkus Pfeiffer 		return (ret);
11482657471SMarkus Pfeiffer 	return (0);
11582657471SMarkus Pfeiffer }
11682657471SMarkus Pfeiffer 
11782657471SMarkus Pfeiffer /* Check if sysv ipc resources are initialized. */
11882657471SMarkus Pfeiffer static int
is_shm_started(void)119ff86f401SSascha Wildner is_shm_started(void)
120ff86f401SSascha Wildner {
12182657471SMarkus Pfeiffer 	if (!is_sysvinit())
12282657471SMarkus Pfeiffer 		return (0);
12382657471SMarkus Pfeiffer 	if (!shmres)
12482657471SMarkus Pfeiffer 		return (0);
12582657471SMarkus Pfeiffer 	return (1);
12682657471SMarkus Pfeiffer }
12782657471SMarkus Pfeiffer 
12882657471SMarkus Pfeiffer /* OBS: It is used only a rwlock for both hashtables and
12982657471SMarkus Pfeiffer  * socket. I've made that choice because is I considered to
13082657471SMarkus Pfeiffer  * be much expensive to acquire/release more than one especially
13182657471SMarkus Pfeiffer  * as the daemon is not multithreading.
13282657471SMarkus Pfeiffer  */
13382657471SMarkus Pfeiffer 
13482657471SMarkus Pfeiffer /* This function has another parameter apart from shmget.
13582657471SMarkus Pfeiffer  * The parameter has information about the type of sysv
13682657471SMarkus Pfeiffer  * ipc resource (shm, sem, msg, undo).
13782657471SMarkus Pfeiffer  * The undo segment is used for sem ops with UNDO flag set.
13882657471SMarkus Pfeiffer  */
13982657471SMarkus Pfeiffer int
_shmget(key_t key,size_t size,int shmflg,int type)140ff86f401SSascha Wildner _shmget(key_t key, size_t size, int shmflg, int type)
141ff86f401SSascha Wildner {
14282657471SMarkus Pfeiffer 	struct shmget_msg msg;
14382657471SMarkus Pfeiffer 	struct shm_data *data;
14482657471SMarkus Pfeiffer 	int shmid, fd;
14582657471SMarkus Pfeiffer 	int flags;
14682657471SMarkus Pfeiffer 
14782657471SMarkus Pfeiffer 	SYSV_MUTEX_LOCK(&lock_resources);
14882657471SMarkus Pfeiffer 	if (shmcheck() < 0) {
14982657471SMarkus Pfeiffer 		sysv_print_err("init sysv ipc\n");
15082657471SMarkus Pfeiffer 		goto done;
15182657471SMarkus Pfeiffer 	}
15282657471SMarkus Pfeiffer 
15382657471SMarkus Pfeiffer 	msg.key = key;
15482657471SMarkus Pfeiffer 	msg.size = size;
15582657471SMarkus Pfeiffer 	msg.shmflg = shmflg;
15682657471SMarkus Pfeiffer 	msg.type = type;
15782657471SMarkus Pfeiffer 
15882657471SMarkus Pfeiffer 	send_message(daemon_fd, type, (char *)&msg, sizeof(msg));
15982657471SMarkus Pfeiffer 
16082657471SMarkus Pfeiffer 	/* Accept a file installed by the daemon.
16182657471SMarkus Pfeiffer 	 * The file is used as shared memory. */
16282657471SMarkus Pfeiffer 	fd = receive_fd(daemon_fd);
16382657471SMarkus Pfeiffer 	if (fd < 0) {
16482657471SMarkus Pfeiffer 		shmid = -1;
16582657471SMarkus Pfeiffer 		goto done;
16682657471SMarkus Pfeiffer 	}
16782657471SMarkus Pfeiffer 
16882657471SMarkus Pfeiffer 	flags = _fcntl(fd, F_GETFD, 0);
16982657471SMarkus Pfeiffer 	if (_fcntl(fd, F_SETFD, flags & FD_CLOEXEC) == -1) {
17082657471SMarkus Pfeiffer 		sysv_print_err("fcntl error\n");
17182657471SMarkus Pfeiffer 		shmid = -1;
17282657471SMarkus Pfeiffer 		goto done;
17382657471SMarkus Pfeiffer 	}
17482657471SMarkus Pfeiffer 
17582657471SMarkus Pfeiffer 	/* Receive the resource id or error. */
17682657471SMarkus Pfeiffer 	receive_message(daemon_fd, (char *)&shmid, sizeof(shmid));
17782657471SMarkus Pfeiffer 
17882657471SMarkus Pfeiffer 	if (shmid < 0) {
17982657471SMarkus Pfeiffer 		errno = -shmid;
18082657471SMarkus Pfeiffer 		shmid = -1;
18182657471SMarkus Pfeiffer 		goto done;
18282657471SMarkus Pfeiffer 	}
18382657471SMarkus Pfeiffer 
18482657471SMarkus Pfeiffer 	/* Do we already have an entry for this resource? */
18582657471SMarkus Pfeiffer 	data = _hash_lookup(shmres, shmid);
18682657471SMarkus Pfeiffer 	if (data)
18782657471SMarkus Pfeiffer 		goto done;
18882657471SMarkus Pfeiffer 
18982657471SMarkus Pfeiffer 	/* If not, add necessary data about it. */
19082657471SMarkus Pfeiffer 	data = malloc(sizeof(struct shm_data));
19182657471SMarkus Pfeiffer 	data->fd = fd;
19282657471SMarkus Pfeiffer 	data->size = size;
19382657471SMarkus Pfeiffer 	data->shmid = shmid;
19482657471SMarkus Pfeiffer 	data->type = type;
19582657471SMarkus Pfeiffer 	data->used = 0;
19682657471SMarkus Pfeiffer 	data->removed = 0;
19782657471SMarkus Pfeiffer 	data->access = 0; /* Used for sems. */
19882657471SMarkus Pfeiffer 
19982657471SMarkus Pfeiffer 	/* Insert data in hashtable using the shmid. */
20082657471SMarkus Pfeiffer 	_hash_insert(shmres, shmid, data);
20182657471SMarkus Pfeiffer done:
20282657471SMarkus Pfeiffer 	SYSV_MUTEX_UNLOCK(&lock_resources);
20382657471SMarkus Pfeiffer 	return (shmid);
20482657471SMarkus Pfeiffer }
20582657471SMarkus Pfeiffer 
20682657471SMarkus Pfeiffer int
sysvipc_shmget(key_t key,size_t size,int shmflg)207ff86f401SSascha Wildner sysvipc_shmget(key_t key, size_t size, int shmflg)
208ff86f401SSascha Wildner {
20982657471SMarkus Pfeiffer 	return (_shmget(key, size, shmflg, SHMGET));
21082657471SMarkus Pfeiffer }
21182657471SMarkus Pfeiffer 
21282657471SMarkus Pfeiffer void *
sysvipc_shmat(int shmid,const void * shmaddr,int shmflg)213ff86f401SSascha Wildner sysvipc_shmat(int shmid, const void *shmaddr, int shmflg)
214ff86f401SSascha Wildner {
21582657471SMarkus Pfeiffer 	struct shmat_msg msg;
21682657471SMarkus Pfeiffer 	void *addr = NULL;
21782657471SMarkus Pfeiffer 	int error;
21882657471SMarkus Pfeiffer 	int flags, prot;
21982657471SMarkus Pfeiffer 	size_t size;
22082657471SMarkus Pfeiffer 	struct shm_data *data;
22182657471SMarkus Pfeiffer 
22282657471SMarkus Pfeiffer 	SYSV_MUTEX_LOCK(&lock_resources);
22382657471SMarkus Pfeiffer 	if (!is_shm_started()) {
22482657471SMarkus Pfeiffer 		errno = EINVAL;
22582657471SMarkus Pfeiffer 		goto done;
22682657471SMarkus Pfeiffer 	}
22782657471SMarkus Pfeiffer 
22882657471SMarkus Pfeiffer 	/* Get data using shmid. */
22982657471SMarkus Pfeiffer 	data = _hash_lookup(shmres, shmid);
23082657471SMarkus Pfeiffer 	if (data == NULL) {
23182657471SMarkus Pfeiffer 		errno = EINVAL;
23282657471SMarkus Pfeiffer 		goto done;
23382657471SMarkus Pfeiffer 	}
23482657471SMarkus Pfeiffer 
23582657471SMarkus Pfeiffer 	size = round_page(data->size);
23682657471SMarkus Pfeiffer 
23782657471SMarkus Pfeiffer #ifdef VM_PROT_READ_IS_EXEC
23882657471SMarkus Pfeiffer 	prot = PROT_READ | PROT_EXECUTE;
23982657471SMarkus Pfeiffer #else
24082657471SMarkus Pfeiffer 	prot = PROT_READ;
24182657471SMarkus Pfeiffer #endif
24282657471SMarkus Pfeiffer 	if ((shmflg & SHM_RDONLY) == 0)
24382657471SMarkus Pfeiffer 		prot |= PROT_WRITE;
24482657471SMarkus Pfeiffer 
24582657471SMarkus Pfeiffer 	flags = MAP_SHARED;
24682657471SMarkus Pfeiffer 	if (shmaddr) {
24782657471SMarkus Pfeiffer 		if (shmflg & SHM_RND) {
248*3f7b7260SSascha Wildner 			addr = (void *)(rounddown2((uintptr_t)shmaddr, SHMLBA));
2497d692414Szrj 		} else if (((uintptr_t)shmaddr & (SHMLBA-1)) == 0) {
25082657471SMarkus Pfeiffer 			addr = __DECONST(void *, shmaddr);
25182657471SMarkus Pfeiffer 		} else {
25282657471SMarkus Pfeiffer 			errno = EINVAL;
25382657471SMarkus Pfeiffer 			goto done;
25482657471SMarkus Pfeiffer 		}
25582657471SMarkus Pfeiffer 	}
25682657471SMarkus Pfeiffer 
25782657471SMarkus Pfeiffer 	msg.shmid = shmid;
25882657471SMarkus Pfeiffer 	msg.shmaddr = shmaddr;
25982657471SMarkus Pfeiffer 	msg.shmflg = shmflg;
26082657471SMarkus Pfeiffer 	msg.size = data->size; /* For undo segment. */
26182657471SMarkus Pfeiffer 
26282657471SMarkus Pfeiffer 	send_message(daemon_fd, SHMAT, (char *)&msg, sizeof(msg));
26382657471SMarkus Pfeiffer 	receive_message(daemon_fd, (char *)&error, sizeof(error));
26482657471SMarkus Pfeiffer 	if (error) {
26582657471SMarkus Pfeiffer 		errno = error;
26682657471SMarkus Pfeiffer 		goto done;
26782657471SMarkus Pfeiffer 	}
26882657471SMarkus Pfeiffer 
26982657471SMarkus Pfeiffer 	addr = mmap(addr, size, prot, flags, data->fd, 0);
27082657471SMarkus Pfeiffer 	if (!addr) {
27182657471SMarkus Pfeiffer 		sysv_print_err("mmap\n");
27282657471SMarkus Pfeiffer 		/* Detach ourselves from the segment. */
27382657471SMarkus Pfeiffer 		send_message(daemon_fd, SHMDT, (char *)&shmid, sizeof(shmid));
27482657471SMarkus Pfeiffer 		goto done;
27582657471SMarkus Pfeiffer 	}
27682657471SMarkus Pfeiffer 
27782657471SMarkus Pfeiffer 	/* Necessary for SEMGET, MSGGET, UNDOGET. */
27882657471SMarkus Pfeiffer 	data->internal = addr;
27982657471SMarkus Pfeiffer 
28082657471SMarkus Pfeiffer 	/* Save the mapped address for munmap call. */
28182657471SMarkus Pfeiffer 	_hash_insert(shmaddrs, (u_long)addr, data);
28282657471SMarkus Pfeiffer done:
28382657471SMarkus Pfeiffer 	SYSV_MUTEX_UNLOCK(&lock_resources);
28482657471SMarkus Pfeiffer 	return (addr);
28582657471SMarkus Pfeiffer }
28682657471SMarkus Pfeiffer 
28782657471SMarkus Pfeiffer /* Remove a sysv ipc resource. */
28882657471SMarkus Pfeiffer static
shmremove(int shmid)289ff86f401SSascha Wildner void shmremove(int shmid)
290ff86f401SSascha Wildner {
29182657471SMarkus Pfeiffer 	struct shm_data *data;
29282657471SMarkus Pfeiffer 	data = _hash_remove(shmres, shmid);
29382657471SMarkus Pfeiffer 
29482657471SMarkus Pfeiffer 	//TODO nu trebuie demapat?
29582657471SMarkus Pfeiffer 	_close(data->fd);
29682657471SMarkus Pfeiffer 	free(data);
29782657471SMarkus Pfeiffer 	data = NULL;
29882657471SMarkus Pfeiffer }
29982657471SMarkus Pfeiffer 
30082657471SMarkus Pfeiffer int
sysvipc_shmctl(int shmid,int cmd,struct shmid_ds * buf)301ff86f401SSascha Wildner sysvipc_shmctl(int shmid, int cmd, struct shmid_ds *buf)
302ff86f401SSascha Wildner {
30382657471SMarkus Pfeiffer 	int size, ret;
30482657471SMarkus Pfeiffer 	struct shmctl_msg *msg;
30582657471SMarkus Pfeiffer 
30682657471SMarkus Pfeiffer /*	if (cmd == IPC_SET)
30782657471SMarkus Pfeiffer 		size = sizeof(struct shmctl_msg) + sizeof(struct shmid_ds);
30882657471SMarkus Pfeiffer 	else
30982657471SMarkus Pfeiffer 		size = sizeof(struct shmctl_msg);
31082657471SMarkus Pfeiffer */
31182657471SMarkus Pfeiffer 	SYSV_MUTEX_LOCK(&lock_resources);
31282657471SMarkus Pfeiffer 
31382657471SMarkus Pfeiffer 	ret = -1;
31482657471SMarkus Pfeiffer 
31582657471SMarkus Pfeiffer 	if (!is_shm_started()) {
31682657471SMarkus Pfeiffer 		errno = EINVAL;
31782657471SMarkus Pfeiffer 		goto done;
31882657471SMarkus Pfeiffer 	}
31982657471SMarkus Pfeiffer 
32082657471SMarkus Pfeiffer 	size = sizeof(struct shmctl_msg);
32182657471SMarkus Pfeiffer 	msg = malloc(size);
32282657471SMarkus Pfeiffer 	msg->shmid = shmid;
32382657471SMarkus Pfeiffer 	msg->cmd = cmd;
32482657471SMarkus Pfeiffer 
32582657471SMarkus Pfeiffer 	if (cmd == IPC_SET)
32682657471SMarkus Pfeiffer 		msg->buf = *buf;
32782657471SMarkus Pfeiffer 
32882657471SMarkus Pfeiffer 	send_message(daemon_fd, SHMCTL, (char *)msg, sizeof(*msg));
32982657471SMarkus Pfeiffer 
33082657471SMarkus Pfeiffer 	receive_message(daemon_fd, (char *)&ret, sizeof(ret));
33182657471SMarkus Pfeiffer 
33282657471SMarkus Pfeiffer 	/* Get data in IPC_STAT case. */
33382657471SMarkus Pfeiffer 	if (ret == 0 && cmd == IPC_STAT)
33482657471SMarkus Pfeiffer 		receive_message(daemon_fd, (char *)buf, sizeof(*buf));
33582657471SMarkus Pfeiffer 
33682657471SMarkus Pfeiffer 	/* Free all resources specific to a shmid in IPC_RMID case. */
33782657471SMarkus Pfeiffer 	if (ret == 0 && cmd == IPC_RMID)
33882657471SMarkus Pfeiffer 		shmremove(shmid);
33982657471SMarkus Pfeiffer 
34082657471SMarkus Pfeiffer 	errno = ret;
34182657471SMarkus Pfeiffer done:
34282657471SMarkus Pfeiffer 	SYSV_MUTEX_UNLOCK(&lock_resources);
34382657471SMarkus Pfeiffer 	return (ret == 0 ? 0 : -1);
34482657471SMarkus Pfeiffer }
34582657471SMarkus Pfeiffer 
34682657471SMarkus Pfeiffer /* Functionality of shmdt with the possibility to inform or not
34782657471SMarkus Pfeiffer  * the daemon.
34882657471SMarkus Pfeiffer  * Inform the daemon when shmdt is called and not when an error
34982657471SMarkus Pfeiffer  * occurs and the daemon doesn't know that the process is attaced.
35082657471SMarkus Pfeiffer  */
35182657471SMarkus Pfeiffer static int
_shmdt(const void * shmaddr,int send_to_daemon)352ff86f401SSascha Wildner _shmdt(const void *shmaddr, int send_to_daemon)
353ff86f401SSascha Wildner {
35482657471SMarkus Pfeiffer 	int ret;
35582657471SMarkus Pfeiffer 	size_t size;
35682657471SMarkus Pfeiffer 	struct shm_data *data;
35782657471SMarkus Pfeiffer 
35882657471SMarkus Pfeiffer 	ret = -1;
35982657471SMarkus Pfeiffer 
36082657471SMarkus Pfeiffer 	SYSV_MUTEX_LOCK(&lock_resources);
36182657471SMarkus Pfeiffer 	if (!is_shm_started()) {
36282657471SMarkus Pfeiffer 		errno = EINVAL;
36382657471SMarkus Pfeiffer 		goto done;
36482657471SMarkus Pfeiffer 	}
36582657471SMarkus Pfeiffer 
36682657471SMarkus Pfeiffer 	/* Verify if shmaddr was returned from a shmat call. */
36782657471SMarkus Pfeiffer 	data = _hash_remove(shmaddrs, (u_long)shmaddr);
36882657471SMarkus Pfeiffer 	if (data == NULL) {
36982657471SMarkus Pfeiffer 		errno = EINVAL;
37082657471SMarkus Pfeiffer 		goto done;
37182657471SMarkus Pfeiffer 	}
37282657471SMarkus Pfeiffer 
37382657471SMarkus Pfeiffer 	size = round_page(data->size);
37482657471SMarkus Pfeiffer 
37582657471SMarkus Pfeiffer 	ret = munmap(__DECONST(void *, shmaddr), size);
37682657471SMarkus Pfeiffer 	if (ret)
37782657471SMarkus Pfeiffer 		goto done;
37882657471SMarkus Pfeiffer 
37982657471SMarkus Pfeiffer 	if (send_to_daemon)
38082657471SMarkus Pfeiffer 		send_message(daemon_fd, SHMDT, (char *)&data->shmid, sizeof(int));
38182657471SMarkus Pfeiffer 
38282657471SMarkus Pfeiffer 	shmaddr = NULL;
38382657471SMarkus Pfeiffer 	free(data);
38482657471SMarkus Pfeiffer 	data = NULL;
38582657471SMarkus Pfeiffer done:
38682657471SMarkus Pfeiffer 	SYSV_MUTEX_UNLOCK(&lock_resources);
38782657471SMarkus Pfeiffer 	return (ret);
38882657471SMarkus Pfeiffer }
38982657471SMarkus Pfeiffer 
39082657471SMarkus Pfeiffer int
sysvipc_shmdt(const void * shmaddr)391ff86f401SSascha Wildner sysvipc_shmdt(const void *shmaddr)
392ff86f401SSascha Wildner {
39382657471SMarkus Pfeiffer 	return (_shmdt(shmaddr, 1));
39482657471SMarkus Pfeiffer }
39582657471SMarkus Pfeiffer 
39682657471SMarkus Pfeiffer void
shmchild(void)397ff86f401SSascha Wildner shmchild(void)
398ff86f401SSascha Wildner {
39982657471SMarkus Pfeiffer 	int i;
40082657471SMarkus Pfeiffer 	struct entries_list *list;
40182657471SMarkus Pfeiffer 	struct hashentry *tmp, *ttmp;
40282657471SMarkus Pfeiffer 	struct shmat_msg msg;
40382657471SMarkus Pfeiffer 	struct shm_data *data;
40482657471SMarkus Pfeiffer 	int error;
40582657471SMarkus Pfeiffer 
40682657471SMarkus Pfeiffer /* OBS: no locking is necessary because this function is called
40782657471SMarkus Pfeiffer  * after the child is created and at that moment only one thread
40882657471SMarkus Pfeiffer  * exists in the process.
40982657471SMarkus Pfeiffer  */
41082657471SMarkus Pfeiffer 	for (i=0; i<get_hash_size(MAXSIZE); i++) {
41182657471SMarkus Pfeiffer 		list = &shmaddrs->entries[i];
41282657471SMarkus Pfeiffer 		if (LIST_EMPTY(list))
41382657471SMarkus Pfeiffer 			continue;
41482657471SMarkus Pfeiffer 		LIST_FOREACH_MUTABLE(tmp, list, entry_link, ttmp) {
41582657471SMarkus Pfeiffer 			data = (struct shm_data*)tmp->value;
41682657471SMarkus Pfeiffer 			/* Inform daemon that we are attached. */
41782657471SMarkus Pfeiffer 
41882657471SMarkus Pfeiffer 			if (data->type == UNDOGET) {
41982657471SMarkus Pfeiffer 				continue;
42082657471SMarkus Pfeiffer 			}
42182657471SMarkus Pfeiffer 
42282657471SMarkus Pfeiffer 			msg.shmid = data->shmid;
42382657471SMarkus Pfeiffer 			msg.shmaddr = data->internal;
42482657471SMarkus Pfeiffer 			msg.shmflg = 0; /* This is enough at this moment. */
42582657471SMarkus Pfeiffer 			msg.size = data->size;
42682657471SMarkus Pfeiffer 			/* Last field is not necessary because it is used only
42782657471SMarkus Pfeiffer 			 * for undo segments.
42882657471SMarkus Pfeiffer 			 */
42982657471SMarkus Pfeiffer 
43082657471SMarkus Pfeiffer 			send_message(daemon_fd, SHMAT, (char *)&msg, sizeof(msg));
43182657471SMarkus Pfeiffer 			receive_message(daemon_fd, (char *)&error, sizeof(error));
43282657471SMarkus Pfeiffer 
43382657471SMarkus Pfeiffer 			/* If the daemon returned error munmap the region. */
43482657471SMarkus Pfeiffer 			if (error) {
43582657471SMarkus Pfeiffer 				errno = error;
43682657471SMarkus Pfeiffer 				_shmdt(data->internal, 0);
43782657471SMarkus Pfeiffer 				shmremove(data->shmid);
43882657471SMarkus Pfeiffer 				sysv_print_err(" %d shmchild\n", error);
43982657471SMarkus Pfeiffer 				sleep(20);
44082657471SMarkus Pfeiffer 			}
44182657471SMarkus Pfeiffer 
44282657471SMarkus Pfeiffer 		}
44382657471SMarkus Pfeiffer 	}
44482657471SMarkus Pfeiffer 
44582657471SMarkus Pfeiffer 	/* Remove semundo structures. Those are specific only for the parent.
44682657471SMarkus Pfeiffer 	 * The child must create for itself a new one.
44782657471SMarkus Pfeiffer 	 */
44882657471SMarkus Pfeiffer 	data = _hash_remove(shmaddrs, (u_long)undos);
44982657471SMarkus Pfeiffer 	if (undos) {
45082657471SMarkus Pfeiffer 		munmap(undos, round_page(data->size));
45182657471SMarkus Pfeiffer 		undos = NULL;
45282657471SMarkus Pfeiffer 	}
45382657471SMarkus Pfeiffer }
45482657471SMarkus Pfeiffer 
45582657471SMarkus Pfeiffer /* Called each time a thread tries to access the sem/msg.
45682657471SMarkus Pfeiffer  * It is used in order to protect data against its removal
45782657471SMarkus Pfeiffer  * by another thread.
45882657471SMarkus Pfeiffer  */
45982657471SMarkus Pfeiffer struct shm_data *
get_shmdata(int id,int to_remove,int shm_access)460ff86f401SSascha Wildner get_shmdata(int id, int to_remove, int shm_access)
461ff86f401SSascha Wildner {
46282657471SMarkus Pfeiffer 	struct shm_data *data = NULL;
46382657471SMarkus Pfeiffer 
46482657471SMarkus Pfeiffer 	SYSV_MUTEX_LOCK(&lock_resources);
46582657471SMarkus Pfeiffer 	if (!is_shm_started()) {
46682657471SMarkus Pfeiffer 		errno = EINVAL;
46782657471SMarkus Pfeiffer 		goto done;
46882657471SMarkus Pfeiffer 	}
46982657471SMarkus Pfeiffer 
47082657471SMarkus Pfeiffer 	data = _hash_lookup(shmres, id);
47182657471SMarkus Pfeiffer 	if (!data) {
47282657471SMarkus Pfeiffer 		errno = EINVAL;
47382657471SMarkus Pfeiffer 		goto done;
47482657471SMarkus Pfeiffer 	}
47582657471SMarkus Pfeiffer 
47682657471SMarkus Pfeiffer 	/* If segment was removed by another thread we can't use it. */
47782657471SMarkus Pfeiffer 	if (data->removed) {
47882657471SMarkus Pfeiffer 		sysv_print("segment already removed\n");
47982657471SMarkus Pfeiffer 		errno = EINVAL;
48082657471SMarkus Pfeiffer 		data = NULL;
48182657471SMarkus Pfeiffer 		goto done;
48282657471SMarkus Pfeiffer 	}
48382657471SMarkus Pfeiffer 
48482657471SMarkus Pfeiffer 	/* Mark for removal. Inform the other threads from the
48582657471SMarkus Pfeiffer 	 * same address space. */
48682657471SMarkus Pfeiffer 	if (to_remove) {
48782657471SMarkus Pfeiffer 		sysv_print("segment is removed\n");
48882657471SMarkus Pfeiffer 		data->removed = to_remove; /* 1 if it is removed by
48982657471SMarkus Pfeiffer 		the current process and 2 if it was removed by
49082657471SMarkus Pfeiffer 		another one. */
49182657471SMarkus Pfeiffer 
49282657471SMarkus Pfeiffer 		/* No need for any rights check because this is
49382657471SMarkus Pfeiffer 		 * done by daemon if this is the process that removes
49482657471SMarkus Pfeiffer 		 * the sem/msg.
49582657471SMarkus Pfeiffer 		 * If not, there is no need for any right to clean
49682657471SMarkus Pfeiffer 		 * internal resources.
49782657471SMarkus Pfeiffer 		 */
49882657471SMarkus Pfeiffer 		goto done2;
49982657471SMarkus Pfeiffer 	}
50082657471SMarkus Pfeiffer 
50182657471SMarkus Pfeiffer 	/* Avoid segmentation fault if the memory zone
50282657471SMarkus Pfeiffer 	 * is accessed without necessary permissions
50382657471SMarkus Pfeiffer 	 * (it was mapped according to them).
50482657471SMarkus Pfeiffer 	 */
50582657471SMarkus Pfeiffer 	if (!(data->access & shm_access)) {
50682657471SMarkus Pfeiffer #if 0
50782657471SMarkus Pfeiffer 		sysv_print("no access rights has %o and wants %o\n",
50882657471SMarkus Pfeiffer 				data->access, shm_access);
50982657471SMarkus Pfeiffer 		errno = EACCES;
51082657471SMarkus Pfeiffer 		data = NULL;
51182657471SMarkus Pfeiffer 		goto done;
51282657471SMarkus Pfeiffer #endif
51382657471SMarkus Pfeiffer 	}
51482657471SMarkus Pfeiffer 
51582657471SMarkus Pfeiffer done2:
51682657471SMarkus Pfeiffer 	data->used++;
51782657471SMarkus Pfeiffer done:
51882657471SMarkus Pfeiffer 	SYSV_MUTEX_UNLOCK(&lock_resources);
51982657471SMarkus Pfeiffer 	return (data);
52082657471SMarkus Pfeiffer }
52182657471SMarkus Pfeiffer 
52282657471SMarkus Pfeiffer /* Set the shm_access type (IPC_R, IPC_W) for sem/msg. */
52382657471SMarkus Pfeiffer int
set_shmdata_access(int id,int shm_access)524ff86f401SSascha Wildner set_shmdata_access(int id, int shm_access)
525ff86f401SSascha Wildner {
52682657471SMarkus Pfeiffer 	struct shm_data *data;
52782657471SMarkus Pfeiffer 	int ret = -1;
52882657471SMarkus Pfeiffer 
52982657471SMarkus Pfeiffer 	SYSV_MUTEX_LOCK(&lock_resources);
53082657471SMarkus Pfeiffer 	if (!is_shm_started()) {
53182657471SMarkus Pfeiffer 		errno = EINVAL;
53282657471SMarkus Pfeiffer 		goto done;
53382657471SMarkus Pfeiffer 	}
53482657471SMarkus Pfeiffer 
53582657471SMarkus Pfeiffer 	data = _hash_lookup(shmres, id);
53682657471SMarkus Pfeiffer 	if (!data) {
53782657471SMarkus Pfeiffer 		errno = EINVAL;
53882657471SMarkus Pfeiffer 		goto done;
53982657471SMarkus Pfeiffer 	}
54082657471SMarkus Pfeiffer 
54182657471SMarkus Pfeiffer 	/* If segment was removed by another thread we can't use it. */
54282657471SMarkus Pfeiffer 	if (data->removed) {
54382657471SMarkus Pfeiffer 		errno = EINVAL;
54482657471SMarkus Pfeiffer 		goto done;
54582657471SMarkus Pfeiffer 	}
54682657471SMarkus Pfeiffer 
54782657471SMarkus Pfeiffer 	data->access = shm_access;
54882657471SMarkus Pfeiffer 	ret = 0;
54982657471SMarkus Pfeiffer done:
55082657471SMarkus Pfeiffer 	SYSV_MUTEX_UNLOCK(&lock_resources);
55182657471SMarkus Pfeiffer 
55282657471SMarkus Pfeiffer 	return (ret);
55382657471SMarkus Pfeiffer }
554