1 /* vifm
2  * Copyright (C) 2018 xaizek.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
17  */
18 
19 #include "shmem.h"
20 
21 #include <sys/mman.h> /* mmap, shm_*() */
22 #include <fcntl.h>    /* O_RDWR, O_* */
23 #include <unistd.h>   /* ftruncate() */
24 
25 #include <errno.h>  /* EEXIST ENOENT errno */
26 #include <stddef.h> /* NULL */
27 #include <stdlib.h> /* malloc() free() */
28 
29 #include "str.h"
30 
31 /* Data of a single shmem instance. */
32 struct shmem_t
33 {
34 	char *name;      /* Name of this object as known to the system. */
35 	int fd;          /* File descriptor obtained from shm_open(). */
36 	int created;     /* This instance was created by us. */
37 	void *ptr;       /* The shared memory as an unstructured blob of bytes. */
38 	size_t max_size; /* Maximum size of shared memory region. */
39 };
40 
41 shmem_t *
shmem_create(const char name[],size_t initial_size,size_t max_size)42 shmem_create(const char name[], size_t initial_size, size_t max_size)
43 {
44 	int error_other;
45 	int error_excl_already_exists;
46 	int error_normal_does_not_exist;
47 	int fd;
48 
49 	shmem_t *const shmem = malloc(sizeof(*shmem));
50 	if(shmem == NULL)
51 	{
52 		return NULL;
53 	}
54 
55 	shmem->name = format_str("/vifm-%s", name);
56 	if(shmem->name == NULL)
57 	{
58 		free(shmem);
59 		return NULL;
60 	}
61 
62 	shmem->ptr = NULL;
63 	shmem->max_size = max_size;
64 
65 	do {
66 		/* Reset errors. */
67 		error_other = 0;
68 		error_excl_already_exists = 0;
69 		error_normal_does_not_exist = 0;
70 
71 		/* Try exclusive access first. */
72 		fd = shm_open(shmem->name, O_RDWR | O_CREAT | O_EXCL, 0600);
73 		if(fd != -1)
74 		{
75 			/* No error exclusive / we know we are init! */
76 			break;
77 		}
78 
79 		/* Error exclusive. */
80 		error_excl_already_exists = (errno == EEXIST);
81 		if(error_excl_already_exists)
82 		{
83 			/* Already exists => try to attach to existing. */
84 			fd = shm_open(shmem->name, O_RDWR, 0600);
85 			if(fd == -1)
86 			{
87 				/* We first got EEXIST now ENOENT => file deleted in the meantime
88 				 * => retry! */
89 				error_normal_does_not_exist = (errno == ENOENT);
90 				error_other = !error_normal_does_not_exist;
91 			}
92 		}
93 		else
94 		{
95 			error_other = 1;
96 		}
97 	}
98 	while(!error_other && error_excl_already_exists &&
99 		error_normal_does_not_exist);
100 
101 	/* Possible cases:
102 	 * (1) error_other                => FAIL
103 	 * (2) error_excl_already_exists  => OK, just attached normally
104 	 * (3) !error_excl_already_exists => OK, responsible for initialization */
105 
106 	if(error_other)
107 	{
108 		free(shmem->name);
109 		free(shmem);
110 		return NULL;
111 	}
112 
113 	shmem->fd = fd;
114 	shmem->created = !error_excl_already_exists;
115 
116 	/* Set initial size if we are the ones who created this shared memory. */
117 	if(shmem->created && ftruncate(fd, initial_size) == -1)
118 	{
119 		shmem_destroy(shmem);
120 		return NULL;
121 	}
122 
123 	shmem->ptr = mmap(NULL, max_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
124 	if(shmem->ptr == MAP_FAILED)
125 	{
126 		if(shmem->created)
127 		{
128 			shmem_destroy(shmem);
129 		}
130 		else
131 		{
132 			shmem_free(shmem);
133 		}
134 		return NULL;
135 	}
136 
137 	return shmem;
138 }
139 
140 void
shmem_destroy(shmem_t * shmem)141 shmem_destroy(shmem_t *shmem)
142 {
143 	if(shmem != NULL)
144 	{
145 		shm_unlink(shmem->name);
146 		shmem_free(shmem);
147 	}
148 }
149 
150 void
shmem_free(shmem_t * shmem)151 shmem_free(shmem_t *shmem)
152 {
153 	if(shmem == NULL)
154 	{
155 		return;
156 	}
157 
158 	if(shmem->ptr != NULL)
159 	{
160 		munmap(shmem->ptr, shmem->max_size);
161 	}
162 
163 	close(shmem->fd);
164 	free(shmem->name);
165 	free(shmem);
166 }
167 
168 int
shmem_created_by_us(shmem_t * shmem)169 shmem_created_by_us(shmem_t *shmem)
170 {
171 	return shmem->created;
172 }
173 
174 void *
shmem_get_ptr(shmem_t * shmem)175 shmem_get_ptr(shmem_t *shmem)
176 {
177 	return shmem->ptr;
178 }
179 
180 int
shmem_resize(shmem_t * shmem,size_t new_size)181 shmem_resize(shmem_t *shmem, size_t new_size)
182 {
183 	return (new_size <= shmem->max_size && ftruncate(shmem->fd, new_size) == 0);
184 }
185 
186 /* vim: set tabstop=2 softtabstop=2 shiftwidth=2 noexpandtab cinoptions-=(0 : */
187 /* vim: set cinoptions+=t0 : */
188