1 /* GIMP - The GNU Image Manipulation Program
2  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3  *
4  * gimppluginhsm.c
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
18  */
19 
20 #include "config.h"
21 
22 #include <sys/types.h>
23 
24 #include <errno.h>
25 
26 #if defined(USE_SYSV_SHM)
27 
28 #ifdef HAVE_IPC_H
29 #include <sys/ipc.h>
30 #endif
31 
32 #ifdef HAVE_SHM_H
33 #include <sys/shm.h>
34 #endif
35 
36 #elif defined(USE_POSIX_SHM)
37 
38 #ifdef HAVE_UNISTD_H
39 #include <unistd.h>
40 #endif
41 
42 #include <fcntl.h>
43 #include <sys/mman.h>
44 
45 #endif /* USE_POSIX_SHM */
46 
47 #include <gio/gio.h>
48 #include <gegl.h>
49 
50 #if defined(G_OS_WIN32) || defined(G_WITH_CYGWIN)
51 
52 #define STRICT
53 #include <windows.h>
54 #include <process.h>
55 
56 #ifdef G_OS_WIN32
57 #include <fcntl.h>
58 #include <io.h>
59 #endif
60 
61 #define USE_WIN32_SHM 1
62 
63 #endif /* G_OS_WIN32 || G_WITH_CYGWIN */
64 
65 #include "plug-in-types.h"
66 
67 #include "core/gimp-utils.h"
68 
69 #include "gimppluginshm.h"
70 
71 #include "gimp-log.h"
72 
73 
74 #define TILE_MAP_SIZE (GIMP_PLUG_IN_TILE_WIDTH * GIMP_PLUG_IN_TILE_HEIGHT * 32)
75 
76 #define ERRMSG_SHM_DISABLE "Disabling shared memory tile transport"
77 
78 
79 struct _GimpPlugInShm
80 {
81   gint    shm_ID;
82   guchar *shm_addr;
83 
84 #if defined(USE_WIN32_SHM)
85   HANDLE  shm_handle;
86 #endif
87 };
88 
89 
90 GimpPlugInShm *
gimp_plug_in_shm_new(void)91 gimp_plug_in_shm_new (void)
92 {
93   /* allocate a piece of shared memory for use in transporting tiles
94    *  to plug-ins. if we can't allocate a piece of shared memory then
95    *  we'll fall back on sending the data over the pipe.
96    */
97 
98   GimpPlugInShm *shm = g_slice_new0 (GimpPlugInShm);
99 
100   shm->shm_ID = -1;
101 
102 #if defined(USE_SYSV_SHM)
103 
104   /* Use SysV shared memory mechanisms for transferring tile data. */
105   {
106     shm->shm_ID = shmget (IPC_PRIVATE, TILE_MAP_SIZE, IPC_CREAT | 0600);
107 
108     if (shm->shm_ID != -1)
109       {
110         shm->shm_addr = (guchar *) shmat (shm->shm_ID, NULL, 0);
111 
112         if (shm->shm_addr == (guchar *) -1)
113           {
114             g_printerr ("shmat() failed: %s\n" ERRMSG_SHM_DISABLE,
115                         g_strerror (errno));
116             shmctl (shm->shm_ID, IPC_RMID, NULL);
117             shm->shm_ID = -1;
118           }
119 
120 #ifdef IPC_RMID_DEFERRED_RELEASE
121         if (shm->shm_addr != (guchar *) -1)
122           shmctl (shm->shm_ID, IPC_RMID, NULL);
123 #endif
124       }
125     else
126       {
127         g_printerr ("shmget() failed: %s\n" ERRMSG_SHM_DISABLE,
128                     g_strerror (errno));
129       }
130   }
131 
132 #elif defined(USE_WIN32_SHM)
133 
134   /* Use Win32 shared memory mechanisms for transferring tile data. */
135   {
136     gint  pid;
137     gchar fileMapName[MAX_PATH];
138 
139     /* Our shared memory id will be our process ID */
140     pid = GetCurrentProcessId ();
141 
142     /* From the id, derive the file map name */
143     g_snprintf (fileMapName, sizeof (fileMapName), "GIMP%d.SHM", pid);
144 
145     /* Create the file mapping into paging space */
146     shm->shm_handle = CreateFileMapping (INVALID_HANDLE_VALUE, NULL,
147                                          PAGE_READWRITE, 0,
148                                          TILE_MAP_SIZE,
149                                          fileMapName);
150 
151     if (shm->shm_handle)
152       {
153         /* Map the shared memory into our address space for use */
154         shm->shm_addr = (guchar *) MapViewOfFile (shm->shm_handle,
155                                                   FILE_MAP_ALL_ACCESS,
156                                                   0, 0, TILE_MAP_SIZE);
157 
158         /* Verify that we mapped our view */
159         if (shm->shm_addr)
160           {
161             shm->shm_ID = pid;
162           }
163         else
164           {
165             g_printerr ("MapViewOfFile error: %d... " ERRMSG_SHM_DISABLE,
166                         GetLastError ());
167           }
168       }
169     else
170       {
171         g_printerr ("CreateFileMapping error: %d... " ERRMSG_SHM_DISABLE,
172                     GetLastError ());
173       }
174   }
175 
176 #elif defined(USE_POSIX_SHM)
177 
178   /* Use POSIX shared memory mechanisms for transferring tile data. */
179   {
180     gint  pid;
181     gchar shm_handle[32];
182     gint  shm_fd;
183 
184     /* Our shared memory id will be our process ID */
185     pid = gimp_get_pid ();
186 
187     /* From the id, derive the file map name */
188     g_snprintf (shm_handle, sizeof (shm_handle), "/gimp-shm-%d", pid);
189 
190     /* Create the file mapping into paging space */
191     shm_fd = shm_open (shm_handle, O_RDWR | O_CREAT, 0600);
192 
193     if (shm_fd != -1)
194       {
195         if (ftruncate (shm_fd, TILE_MAP_SIZE) != -1)
196           {
197             /* Map the shared memory into our address space for use */
198             shm->shm_addr = (guchar *) mmap (NULL, TILE_MAP_SIZE,
199                                              PROT_READ | PROT_WRITE, MAP_SHARED,
200                                              shm_fd, 0);
201 
202             /* Verify that we mapped our view */
203             if (shm->shm_addr != MAP_FAILED)
204               {
205                 shm->shm_ID = pid;
206               }
207             else
208               {
209                 g_printerr ("mmap() failed: %s\n" ERRMSG_SHM_DISABLE,
210                             g_strerror (errno));
211 
212                 shm_unlink (shm_handle);
213               }
214           }
215         else
216           {
217             g_printerr ("ftruncate() failed: %s\n" ERRMSG_SHM_DISABLE,
218                         g_strerror (errno));
219 
220             shm_unlink (shm_handle);
221           }
222 
223         close (shm_fd);
224       }
225     else
226       {
227         g_printerr ("shm_open() failed: %s\n" ERRMSG_SHM_DISABLE,
228                     g_strerror (errno));
229       }
230   }
231 
232 #endif
233 
234   if (shm->shm_ID == -1)
235     {
236       g_slice_free (GimpPlugInShm, shm);
237       shm = NULL;
238     }
239   else
240     {
241       GIMP_LOG (SHM, "attached shared memory segment ID = %d", shm->shm_ID);
242     }
243 
244   return shm;
245 }
246 
247 void
gimp_plug_in_shm_free(GimpPlugInShm * shm)248 gimp_plug_in_shm_free (GimpPlugInShm *shm)
249 {
250   g_return_if_fail (shm != NULL);
251 
252   if (shm->shm_ID != -1)
253     {
254 
255 #if defined (USE_SYSV_SHM)
256 
257       shmdt (shm->shm_addr);
258 
259 #ifndef IPC_RMID_DEFERRED_RELEASE
260       shmctl (shm->shm_ID, IPC_RMID, NULL);
261 #endif
262 
263 #elif defined(USE_WIN32_SHM)
264 
265       if (shm->shm_handle)
266         CloseHandle (shm->shm_handle);
267 
268 #elif defined(USE_POSIX_SHM)
269 
270       gchar shm_handle[32];
271 
272       munmap (shm->shm_addr, TILE_MAP_SIZE);
273 
274       g_snprintf (shm_handle, sizeof (shm_handle), "/gimp-shm-%d",
275                   shm->shm_ID);
276 
277       shm_unlink (shm_handle);
278 
279 #endif
280 
281       GIMP_LOG (SHM, "detached shared memory segment ID = %d", shm->shm_ID);
282     }
283 
284   g_slice_free (GimpPlugInShm, shm);
285 }
286 
287 gint
gimp_plug_in_shm_get_ID(GimpPlugInShm * shm)288 gimp_plug_in_shm_get_ID (GimpPlugInShm *shm)
289 {
290   g_return_val_if_fail (shm != NULL, -1);
291 
292   return shm->shm_ID;
293 }
294 
295 guchar *
gimp_plug_in_shm_get_addr(GimpPlugInShm * shm)296 gimp_plug_in_shm_get_addr (GimpPlugInShm *shm)
297 {
298   g_return_val_if_fail (shm != NULL, NULL);
299 
300   return shm->shm_addr;
301 }
302