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